summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/build_helper/src/ci.rs8
-rw-r--r--src/tools/build_helper/src/lib.rs1
-rw-r--r--src/tools/build_helper/src/util.rs45
-rw-r--r--src/tools/bump-stage0/Cargo.toml2
-rw-r--r--src/tools/cargo/.github/workflows/main.yml9
-rw-r--r--src/tools/cargo/CHANGELOG.md228
-rw-r--r--src/tools/cargo/Cargo.lock677
-rw-r--r--src/tools/cargo/Cargo.toml21
-rw-r--r--src/tools/cargo/README.md2
-rw-r--r--src/tools/cargo/clippy.toml2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/containers.rs4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/lib.rs6
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/paths.rs21
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/registry.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/tools.rs15
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs17
-rw-r--r--src/tools/cargo/crates/resolver-tests/Cargo.toml1
-rw-r--r--src/tools/cargo/crates/resolver-tests/src/lib.rs4
-rw-r--r--src/tools/cargo/crates/resolver-tests/tests/resolve.rs4
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/README.md7
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/README.md7
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs7
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs190
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs194
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/README.md7
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/README.md7
-rw-r--r--src/tools/cargo/src/bin/cargo/cli.rs162
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/add.rs13
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/install.rs11
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/mod.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/run.rs90
-rw-r--r--src/tools/cargo/src/bin/cargo/main.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/build_config.rs22
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/custom_build.rs59
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs5
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/mod.rs208
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/features.rs166
-rw-r--r--src/tools/cargo/src/cargo/core/manifest.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/package.rs41
-rw-r--r--src/tools/cargo/src/cargo/core/package_id.rs16
-rw-r--r--src/tools/cargo/src/cargo/core/profiles.rs56
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/encode.rs9
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/features.rs3
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/resolve.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/version_prefs.rs3
-rw-r--r--src/tools/cargo/src/cargo/core/shell.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/source/mod.rs27
-rw-r--r--src/tools/cargo/src/cargo/core/source/source_id.rs10
-rw-r--r--src/tools/cargo/src/cargo/core/summary.rs68
-rw-r--r--src/tools/cargo/src/cargo/core/workspace.rs48
-rw-r--r--src/tools/cargo/src/cargo/lib.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_clean.rs7
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs6
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_fetch.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_install.rs4
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_new.rs13
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_package.rs11
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_test.rs13
-rw-r--r--src/tools/cargo/src/cargo/ops/lockfile.rs43
-rw-r--r--src/tools/cargo/src/cargo/ops/mod.rs14
-rw-r--r--src/tools/cargo/src/cargo/ops/registry.rs1227
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/login.rs160
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/logout.rs42
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/mod.rs213
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/owner.rs93
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/publish.rs461
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/search.rs95
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/yank.rs76
-rw-r--r--src/tools/cargo/src/cargo/sources/config.rs4
-rw-r--r--src/tools/cargo/src/cargo/sources/git/known_hosts.rs5
-rw-r--r--src/tools/cargo/src/cargo/sources/git/mod.rs10
-rw-r--r--src/tools/cargo/src/cargo/sources/git/oxide.rs4
-rw-r--r--src/tools/cargo/src/cargo/sources/git/source.rs73
-rw-r--r--src/tools/cargo/src/cargo/sources/git/utils.rs445
-rw-r--r--src/tools/cargo/src/cargo/sources/path.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/download.rs42
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/http_remote.rs84
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/index.rs533
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/local.rs60
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/mod.rs415
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/remote.rs126
-rw-r--r--src/tools/cargo/src/cargo/util/auth/asymmetric.rs155
-rw-r--r--src/tools/cargo/src/cargo/util/auth/mod.rs (renamed from src/tools/cargo/src/cargo/util/auth.rs)129
-rw-r--r--src/tools/cargo/src/cargo/util/command_prelude.rs61
-rw-r--r--src/tools/cargo/src/cargo/util/config/mod.rs41
-rw-r--r--src/tools/cargo/src/cargo/util/config/target.rs18
-rw-r--r--src/tools/cargo/src/cargo/util/interning.rs10
-rw-r--r--src/tools/cargo/src/cargo/util/network/http.rs216
-rw-r--r--src/tools/cargo/src/cargo/util/network/mod.rs40
-rw-r--r--src/tools/cargo/src/cargo/util/network/retry.rs28
-rw-r--r--src/tools/cargo/src/cargo/util/profile.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/restricted_names.rs24
-rw-r--r--src/tools/cargo/src/cargo/util/toml/embedded.rs895
-rw-r--r--src/tools/cargo/src/cargo/util/toml/mod.rs263
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/manifest.rs11
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/index.md10
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-install.md4
-rw-r--r--src/tools/cargo/src/doc/man/cargo-test.md13
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt6
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt1
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt18
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-jobs.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-bench.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-build.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-check.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-doc.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-fix.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-install.md7
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-package.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-publish.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-run.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustc.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-test.md16
-rw-r--r--src/tools/cargo/src/doc/src/faq.md46
-rw-r--r--src/tools/cargo/src/doc/src/reference/config.md5
-rw-r--r--src/tools/cargo/src/doc/src/reference/environment-variables.md1
-rw-r--r--src/tools/cargo/src/doc/src/reference/registry-index.md10
-rw-r--r--src/tools/cargo/src/doc/src/reference/resolver.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/specifying-dependencies.md40
-rw-r--r--src/tools/cargo/src/doc/src/reference/unstable.md170
-rw-r--r--src/tools/cargo/src/etc/man/cargo-bench.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-build.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-check.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-doc.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-fix.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-install.17
-rw-r--r--src/tools/cargo/src/etc/man/cargo-package.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-publish.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-run.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustc.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustdoc.13
-rw-r--r--src/tools/cargo/src/etc/man/cargo-test.116
-rw-r--r--src/tools/cargo/tests/internal.rs107
-rw-r--r--src/tools/cargo/tests/testsuite/bench.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/build.rs24
-rw-r--r--src/tools/cargo/tests/testsuite/build_script.rs7
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs20
l---------src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/in (renamed from src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/mod.rs (renamed from src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/mod.rs)9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stdout.log (renamed from src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/mod.rs95
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs31
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log7
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs15
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_features.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs24
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/src/lib.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/mod.rs58
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/config.rs62
-rw-r--r--src/tools/cargo/tests/testsuite/config_include.rs112
-rw-r--r--src/tools/cargo/tests/testsuite/cross_compile.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/custom_target.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/directory.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/doc.rs98
-rw-r--r--src/tools/cargo/tests/testsuite/features.rs99
-rw-r--r--src/tools/cargo/tests/testsuite/features2.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/future_incompat_report.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/git.rs68
-rw-r--r--src/tools/cargo/tests/testsuite/install.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/lockfile_compat.rs78
-rw-r--r--src/tools/cargo/tests/testsuite/main.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/profile_config.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/profile_targets.rs51
-rw-r--r--src/tools/cargo/tests/testsuite/profiles.rs52
-rw-r--r--src/tools/cargo/tests/testsuite/required_features.rs7
-rw-r--r--src/tools/cargo/tests/testsuite/run.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/rustc.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/script.rs1184
-rw-r--r--src/tools/cargo/tests/testsuite/test.rs11
-rw-r--r--src/tools/cargo/triagebot.toml16
-rw-r--r--src/tools/clippy/.cargo/config.toml2
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml23
-rw-r--r--src/tools/clippy/.github/workflows/clippy.yml1
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml1
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml1
-rw-r--r--src/tools/clippy/CHANGELOG.md324
-rw-r--r--src/tools/clippy/Cargo.toml28
-rw-r--r--src/tools/clippy/book/src/configuration.md10
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md29
-rw-r--r--src/tools/clippy/book/src/development/basics.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/changelog_update.md22
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md591
-rw-r--r--src/tools/clippy/clippy_dev/src/bless.rs60
-rw-r--r--src/tools/clippy/clippy_dev/src/fmt.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs7
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs14
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs15
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml13
-rw-r--r--src/tools/clippy/clippy_lints/src/allow_attributes.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs79
-rw-r--r--src/tools/clippy/clippy_lints/src/as_conversions.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs68
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs140
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs76
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs216
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_variants.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_nesting.rs181
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format_push_string.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/formatting.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/incorrect_impls.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs74
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_arrays.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_frames.rs162
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs166
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs69
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs256
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs86
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/same_item_push.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs319
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs123
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_forget.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/drain_collect.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_nth.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs176
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs115
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs211
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs126
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs153
-rw-r--r--src/tools/clippy/clippy_lints/src/minmax.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/redundant_at_rest_pattern.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs238
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_else.rs61
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_if.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs48
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/eq_op.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs113
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs250
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs143
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs163
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs213
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs587
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs133
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs147
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs235
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs126
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs133
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs256
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs156
-rw-r--r--src/tools/clippy/clippy_lints/src/visibility.rs131
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs11
-rw-r--r--src/tools/clippy/clippy_test_deps/Cargo.toml23
-rw-r--r--src/tools/clippy/clippy_test_deps/src/lib.rs14
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs122
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs150
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs23
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/mod.rs23
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/numeric_literal.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs123
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs82
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs1
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/declare_clippy_lint/src/lib.rs4
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml2
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/rustfmt.toml1
-rw-r--r--src/tools/clippy/src/driver.rs21
-rw-r--r--src/tools/clippy/src/main.rs8
-rw-r--r--src/tools/clippy/tests/compile-test.rs436
-rw-r--r--src/tools/clippy/tests/headers.rs29
-rw-r--r--src/tools/clippy/tests/lint_message_convention.rs30
-rw-r--r--src/tools/clippy/tests/missing-test-files.rs6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr (renamed from src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr)7
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr (renamed from src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr)7
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr (renamed from src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr)7
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.stderr21
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr22
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr20
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr20
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr20
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr (renamed from src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr)15
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.stderr (renamed from src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr)2
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr52
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr53
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr43
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr44
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr18
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr10
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr11
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr10
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr11
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr4
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock67
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr5
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr6
-rwxr-xr-xsrc/tools/clippy/tests/ui-cargo/update-all-references.sh2
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr5
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr6
-rw-r--r--src/tools/clippy/tests/ui-internal/check_formulation.rs54
-rw-r--r--src/tools/clippy/tests/ui-internal/check_formulation.stderr19
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.rs2
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.stderr5
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.rs7
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.stderr20
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed1
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr12
-rw-r--r--src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr18
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr12
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr12
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml4
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs472
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs197
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr314
-rw-r--r--src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs2
-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/lint_decimal_readability/test.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/min_ident_chars/auxiliary/extern_types.rs3
-rw-r--r--src/tools/clippy/tests/ui-toml/min_ident_chars/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.rs19
-rw-r--r--src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.stderr46
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs4
-rw-r--r--src/tools/clippy/tests/ui-toml/module_inception/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/module_inception/module_inception.rs34
-rw-r--r--src/tools/clippy/tests/ui-toml/module_inception/module_inception.stderr20
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs5
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed2
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr18
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr28
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml4
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr86
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs13
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs567
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr314
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs9
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr62
-rwxr-xr-xsrc/tools/clippy/tests/ui-toml/update-all-references.sh2
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.fixed20
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.rs20
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.stderr4
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_false_positive.rs5
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_without_reason.rs32
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr28
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_range.fixed2
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/tests/ui/arc_with_non_send_sync.rs24
-rw-r--r--src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr34
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs17
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.rs13
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.stderr6
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut.rs2
-rw-r--r--src/tools/clippy/tests/ui/asm_syntax.rs4
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.fixed1
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.rs1
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.stderr14
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.fixed2
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.rs2
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/extern_fake_libc.rs10
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_rules.rs8
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs2
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs4
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs114
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs5
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs5
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macros.rs6
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs6
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed2
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions.rs2
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs7
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr4
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.fixed1
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.rs1
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.stderr44
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.fixed9
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.rs9
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.stderr4
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.fixed2
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.rs2
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs26
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr24
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs30
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr34
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs32
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr36
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs6
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr20
-rw-r--r--src/tools/clippy/tests/ui/builtin_type_shadow.rs2
-rw-r--r--src/tools/clippy/tests/ui/bytecount.rs2
-rw-r--r--src/tools/clippy/tests/ui/cast.rs8
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr118
-rw-r--r--src/tools/clippy/tests/ui/cast_ref_to_mut.rs31
-rw-r--r--src/tools/clippy/tests/ui/cast_ref_to_mut.stderr22
-rw-r--r--src/tools/clippy/tests/ui/cast_slice_different_sizes.rs2
-rw-r--r--src/tools/clippy/tests/ui/cfg_features.rs12
-rw-r--r--src/tools/clippy/tests/ui/cfg_features.stderr28
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs6
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr40
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs6
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr4
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs6
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr34
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy_impl.rs2
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed1
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.rs1
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr16
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.rs34
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.stderr148
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed7
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs7
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr12
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed2
-rw-r--r--src/tools/clippy/tests/ui/cognitive_complexity.stderr10
-rw-r--r--src/tools/clippy/tests/ui/collapsible_else_if.fixed2
-rw-r--r--src/tools/clippy/tests/ui/collapsible_else_if.rs2
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.fixed1
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.rs1
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.stderr18
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs2
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.fixed1
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.rs1
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.stderr8
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10645.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10645.stderr4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10912.rs4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10912.stderr16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11065.rs19
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-1782.rs1
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2774.stderr4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3462.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3741.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4968.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5497.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5579.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6250.stderr16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6251.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6256.rs4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6256.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7169.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7169.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7410.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9445.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr4
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs2
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr11
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs2
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro.rs1
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro.stderr36
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs27
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr43
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs16
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr8
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs22
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr22
-rw-r--r--src/tools/clippy/tests/ui/def_id_nocore.rs2
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed2
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs2
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed2
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs2
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.fixed2
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.rs2
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.fixed4
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.rs4
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof_macro.rs2
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed21
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs21
-rw-r--r--src/tools/clippy/tests/ui/derive.rs2
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.rs1
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.stderr28
-rw-r--r--src/tools/clippy/tests/ui/diverging_sub_expression.rs16
-rw-r--r--src/tools/clippy/tests/ui/diverging_sub_expression.stderr46
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr32
-rw-r--r--src/tools/clippy/tests/ui/doc/needless_doctest_main.rs20
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.rs2
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.fixed1
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.rs1
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.stderr16
-rw-r--r--src/tools/clippy/tests/ui/drain_collect.fixed77
-rw-r--r--src/tools/clippy/tests/ui/drain_collect.rs77
-rw-r--r--src/tools/clippy/tests/ui/drain_collect.stderr68
-rw-r--r--src/tools/clippy/tests/ui/else_if_without_else.rs4
-rw-r--r--src/tools/clippy/tests/ui/else_if_without_else.stderr4
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs5
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs5
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.rs2
-rw-r--r--src/tools/clippy/tests/ui/empty_loop_no_std.rs2
-rw-r--r--src/tools/clippy/tests/ui/endian_bytes.rs127
-rw-r--r--src/tools/clippy/tests/ui/endian_bytes.stderr1031
-rw-r--r--src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs2
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.rs2
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.stderr12
-rw-r--r--src/tools/clippy/tests/ui/eq_op.rs4
-rw-r--r--src/tools/clippy/tests/ui/eq_op.stderr20
-rw-r--r--src/tools/clippy/tests/ui/eq_op_macros.rs1
-rw-r--r--src/tools/clippy/tests/ui/eq_op_macros.stderr24
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.fixed11
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.rs11
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.stderr28
-rw-r--r--src/tools/clippy/tests/ui/err_expect.fixed2
-rw-r--r--src/tools/clippy/tests/ui/err_expect.rs2
-rw-r--r--src/tools/clippy/tests/ui/eta.fixed9
-rw-r--r--src/tools/clippy/tests/ui/eta.rs9
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr52
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.fixed15
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.rs15
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.stderr30
-rw-r--r--src/tools/clippy/tests/ui/expect.rs1
-rw-r--r--src/tools/clippy/tests/ui/expect.stderr6
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.fixed6
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.rs6
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.stderr30
-rw-r--r--src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs2
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.rs50
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.stderr10
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.fixed33
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.rs33
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.stderr24
-rw-r--r--src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed69
-rw-r--r--src/tools/clippy/tests/ui/explicit_into_iter_loop.rs69
-rw-r--r--src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr40
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.fixed154
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.rs154
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.stderr142
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.rs2
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed12
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.rs12
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr16
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.rs4
-rw-r--r--src/tools/clippy/tests/ui/filetype_is_file.rs1
-rw-r--r--src/tools/clippy/tests/ui/filetype_is_file.stderr6
-rw-r--r--src/tools/clippy/tests/ui/find_map.rs1
-rw-r--r--src/tools/clippy/tests/ui/fn_null_check.rs1
-rw-r--r--src/tools/clippy/tests/ui/fn_null_check.stderr10
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.fixed309
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.rs309
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.stderr96
-rw-r--r--src/tools/clippy/tests/ui/for_loop_unfixable.rs16
-rw-r--r--src/tools/clippy/tests/ui/for_loop_unfixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/format.fixed4
-rw-r--r--src/tools/clippy/tests/ui/format.rs4
-rw-r--r--src/tools/clippy/tests/ui/format.stderr30
-rw-r--r--src/tools/clippy/tests/ui/format_push_string.rs29
-rw-r--r--src/tools/clippy/tests/ui/format_push_string.stderr37
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed1
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs1
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr30
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.fixed15
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.rs15
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.stderr18
-rw-r--r--src/tools/clippy/tests/ui/from_over_into_unfixable.rs16
-rw-r--r--src/tools/clippy/tests/ui/from_over_into_unfixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs1
-rw-r--r--src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr20
-rw-r--r--src/tools/clippy/tests/ui/get_first.fixed1
-rw-r--r--src/tools/clippy/tests/ui/get_first.rs1
-rw-r--r--src/tools/clippy/tests/ui/get_first.stderr6
-rw-r--r--src/tools/clippy/tests/ui/get_last_with_len.fixed2
-rw-r--r--src/tools/clippy/tests/ui/get_last_with_len.rs2
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.fixed46
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.rs46
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.stderr80
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.rs10
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.stderr24
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else2.rs12
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else2.stderr37
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.rs14
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.stderr16
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.rs2
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed2
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs2
-rw-r--r--src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed97
-rw-r--r--src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs107
-rw-r--r--src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr40
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.rs7
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr26
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_slice.rs2
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.fixed54
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.rs54
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.stderr54
-rw-r--r--src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs20
-rw-r--r--src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr22
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.rs2
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.stderr2
-rw-r--r--src/tools/clippy/tests/ui/issue_4266.stderr8
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr17
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.fixed1
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.rs1
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.stderr10
-rw-r--r--src/tools/clippy/tests/ui/iter_count.fixed3
-rw-r--r--src/tools/clippy/tests/ui/iter_count.rs3
-rw-r--r--src/tools/clippy/tests/ui/iter_count.stderr50
-rw-r--r--src/tools/clippy/tests/ui/iter_next_loop.rs16
-rw-r--r--src/tools/clippy/tests/ui/iter_next_loop.stderr9
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.fixed1
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.rs1
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.stderr8
-rw-r--r--src/tools/clippy/tests/ui/iter_nth.rs1
-rw-r--r--src/tools/clippy/tests/ui/iter_nth.stderr22
-rw-r--r--src/tools/clippy/tests/ui/iter_nth_zero.fixed15
-rw-r--r--src/tools/clippy/tests/ui/iter_nth_zero.rs15
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.fixed2
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.rs2
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.fixed1
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.rs1
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.stderr14
-rw-r--r--src/tools/clippy/tests/ui/iter_with_drain.fixed2
-rw-r--r--src/tools/clippy/tests/ui/iter_with_drain.rs2
-rw-r--r--src/tools/clippy/tests/ui/iterator_step_by_zero.rs1
-rw-r--r--src/tools/clippy/tests/ui/iterator_step_by_zero.stderr14
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.rs2
-rw-r--r--src/tools/clippy/tests/ui/large_futures.rs1
-rw-r--r--src/tools/clippy/tests/ui/large_futures.stderr16
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.rs13
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.stderr30
-rw-r--r--src/tools/clippy/tests/ui/large_stack_frames.rs44
-rw-r--r--src/tools/clippy/tests/ui/large_stack_frames.stderr37
-rw-r--r--src/tools/clippy/tests/ui/len_zero.fixed2
-rw-r--r--src/tools/clippy/tests/ui/len_zero.rs2
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_untyped.rs2
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.rs2
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.fixed4
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.rs4
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.stderr22
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.fixed2
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.rs2
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr16
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports_expect.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.rs2
-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.stderr54
-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.stderr60
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.rs66
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.stderr130
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.rs13
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.stderr31
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr26
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.fixed35
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.rs35
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.stderr51
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_try_fold.rs100
-rw-r--r--src/tools/clippy/tests/ui/manual_try_fold.stderr28
-rw-r--r--src/tools/clippy/tests/ui/manual_unwrap_or.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_unwrap_or.rs2
-rw-r--r--src/tools/clippy/tests/ui/map_clone.fixed3
-rw-r--r--src/tools/clippy/tests/ui/map_clone.rs3
-rw-r--r--src/tools/clippy/tests/ui/map_clone.stderr12
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.rs47
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.stderr44
-rw-r--r--src/tools/clippy/tests/ui/match_on_vec_items.rs1
-rw-r--r--src/tools/clippy/tests/ui/match_on_vec_items.stderr16
-rw-r--r--src/tools/clippy/tests/ui/match_overlapping_arm.rs2
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.rs20
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.stderr24
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.rs23
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.stderr33
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs58
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr29
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed3
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs3
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr48
-rw-r--r--src/tools/clippy/tests/ui/match_wild_err_arm.rs15
-rw-r--r--src/tools/clippy/tests/ui/match_wild_err_arm.stderr8
-rw-r--r--src/tools/clippy/tests/ui/mem_forget.rs3
-rw-r--r--src/tools/clippy/tests/ui/mem_forget.stderr15
-rw-r--r--src/tools/clippy/tests/ui/mem_replace_macro.rs2
-rw-r--r--src/tools/clippy/tests/ui/methods.rs1
-rw-r--r--src/tools/clippy/tests/ui/methods.stderr4
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.stderr2
-rw-r--r--src/tools/clippy/tests/ui/min_ident_chars.rs84
-rw-r--r--src/tools/clippy/tests/ui/min_ident_chars.stderr178
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.rs11
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.stderr32
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs45
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs13
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr30
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.rs2
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.rs2
-rw-r--r--src/tools/clippy/tests/ui/missing_fields_in_debug.rs191
-rw-r--r--src/tools/clippy/tests/ui/missing_fields_in_debug.stderr73
-rw-r--r--src/tools/clippy/tests/ui/missing_inline_proc_macro.rs1
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.rs80
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.stderr110
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed2
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.rs2
-rw-r--r--src/tools/clippy/tests/ui/module_inception.rs12
-rw-r--r--src/tools/clippy/tests/ui/module_inception.stderr22
-rw-r--r--src/tools/clippy/tests/ui/modulo_one.stderr6
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs2
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.fixed2
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.rs2
-rw-r--r--src/tools/clippy/tests/ui/mut_mut.rs2
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs5
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.stderr42
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed15
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs15
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr72
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.fixed3
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.stderr34
-rw-r--r--src/tools/clippy/tests/ui/needless_collect.fixed7
-rw-r--r--src/tools/clippy/tests/ui/needless_collect.rs7
-rw-r--r--src/tools/clippy/tests/ui/needless_collect_indirect.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_collect_indirect.stderr32
-rw-r--r--src/tools/clippy/tests/ui/needless_else.fixed57
-rw-r--r--src/tools/clippy/tests/ui/needless_else.rs58
-rw-r--r--src/tools/clippy/tests/ui/needless_else.stderr12
-rw-r--r--src/tools/clippy/tests/ui/needless_if.fixed93
-rw-r--r--src/tools/clippy/tests/ui/needless_if.rs94
-rw-r--r--src/tools/clippy/tests/ui/needless_if.stderr65
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.fixed5
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs5
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.stderr32
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.fixed2
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.rs2
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.stderr184
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.fixed1
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.stderr6
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_pub_self.fixed33
-rw-r--r--src/tools/clippy/tests/ui/needless_pub_self.rs33
-rw-r--r--src/tools/clippy/tests/ui/needless_pub_self.stderr22
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.rs64
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.stderr28
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop2.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop2.stderr16
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.fixed17
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.rs17
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.stderr16
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed20
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.rs20
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr40
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed3
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr426
-rw-r--r--src/tools/clippy/tests/ui/never_loop.rs38
-rw-r--r--src/tools/clippy/tests/ui/never_loop.stderr46
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs22
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.stderr18
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs26
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr9
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs3
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr58
-rw-r--r--src/tools/clippy/tests/ui/no_effect_return.rs81
-rw-r--r--src/tools/clippy/tests/ui/no_effect_return.stderr70
-rw-r--r--src/tools/clippy/tests/ui/non_expressive_names.rs12
-rw-r--r--src/tools/clippy/tests/ui/non_expressive_names.stderr6
-rw-r--r--src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed2
-rw-r--r--src/tools/clippy/tests/ui/non_octal_unix_permissions.rs2
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs17
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr26
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed2
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.rs2
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.stderr6
-rw-r--r--src/tools/clippy/tests/ui/ok_expect.rs2
-rw-r--r--src/tools/clippy/tests/ui/ok_expect.stderr10
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.fixed2
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.rs2
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.rs2
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed16
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs22
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr20
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed8
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs8
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr56
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.fixed2
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.rs2
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.rs1
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.stderr16
-rw-r--r--src/tools/clippy/tests/ui/partialeq_to_none.fixed2
-rw-r--r--src/tools/clippy/tests/ui/partialeq_to_none.rs2
-rw-r--r--src/tools/clippy/tests/ui/patterns.fixed11
-rw-r--r--src/tools/clippy/tests/ui/patterns.rs11
-rw-r--r--src/tools/clippy/tests/ui/patterns.stderr6
-rw-r--r--src/tools/clippy/tests/ui/print_with_newline.fixed58
-rw-r--r--src/tools/clippy/tests/ui/print_with_newline.rs2
-rw-r--r--src/tools/clippy/tests/ui/print_with_newline.stderr12
-rw-r--r--src/tools/clippy/tests/ui/proc_macro.rs1
-rw-r--r--src/tools/clippy/tests/ui/proc_macro.stderr2
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs33
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr60
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed2
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs2
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.fixed62
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.rs62
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.stderr46
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed2
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.rs2
-rw-r--r--src/tools/clippy/tests/ui/pub_with_shorthand.fixed38
-rw-r--r--src/tools/clippy/tests/ui/pub_with_shorthand.rs38
-rw-r--r--src/tools/clippy/tests/ui/pub_with_shorthand.stderr28
-rw-r--r--src/tools/clippy/tests/ui/pub_without_shorthand.fixed38
-rw-r--r--src/tools/clippy/tests/ui/pub_without_shorthand.rs38
-rw-r--r--src/tools/clippy/tests/ui/pub_without_shorthand.stderr22
-rw-r--r--src/tools/clippy/tests/ui/question_mark.fixed22
-rw-r--r--src/tools/clippy/tests/ui/question_mark.rs26
-rw-r--r--src/tools/clippy/tests/ui/question_mark.stderr42
-rw-r--r--src/tools/clippy/tests/ui/range.rs1
-rw-r--r--src/tools/clippy/tests/ui/range.stderr2
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs1
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr8
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs1
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr8
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs1
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr16
-rw-r--r--src/tools/clippy/tests/ui/redundant_at_rest_pattern.fixed27
-rw-r--r--src/tools/clippy/tests/ui/redundant_at_rest_pattern.rs27
-rw-r--r--src/tools/clippy/tests/ui/redundant_at_rest_pattern.stderr40
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.fixed7
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.rs7
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.stderr60
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed47
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs47
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr62
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed7
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs7
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr44
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr36
-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.stderr66
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr36
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed16
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs16
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr66
-rw-r--r--src/tools/clippy/tests/ui/redundant_pub_crate.fixed8
-rw-r--r--src/tools/clippy/tests/ui/redundant_pub_crate.rs8
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed22
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.rs22
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr24
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs6
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr12
-rw-r--r--src/tools/clippy/tests/ui/redundant_type_annotations.rs190
-rw-r--r--src/tools/clippy/tests/ui/redundant_type_annotations.stderr106
-rw-r--r--src/tools/clippy/tests/ui/regex.rs14
-rw-r--r--src/tools/clippy/tests/ui/regex.stderr58
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed9
-rw-r--r--src/tools/clippy/tests/ui/rename.rs9
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr122
-rw-r--r--src/tools/clippy/tests/ui/same_functions_in_if_condition.rs16
-rw-r--r--src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr24
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.rs1
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.stderr16
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.rs2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.rs2
-rw-r--r--src/tools/clippy/tests/ui/self_assignment.rs1
-rw-r--r--src/tools/clippy/tests/ui/self_assignment.stderr22
-rw-r--r--src/tools/clippy/tests/ui/shadow.rs4
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.fixed627
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_tightening.fixed12
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_tightening.rs12
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_tightening.stderr6
-rw-r--r--src/tools/clippy/tests/ui/single_call_fn.rs74
-rw-r--r--src/tools/clippy/tests/ui/single_call_fn.stderr55
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.fixed1
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.rs1
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.stderr30
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.fixed2
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.rs2
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.stderr4
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.fixed2
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.rs2
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.stderr14
-rw-r--r--src/tools/clippy/tests/ui/single_match.fixed254
-rw-r--r--src/tools/clippy/tests/ui/single_match.rs70
-rw-r--r--src/tools/clippy/tests/ui/single_match.stderr76
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.fixed173
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.rs90
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.stderr98
-rw-r--r--src/tools/clippy/tests/ui/single_range_in_vec_init.rs58
-rw-r--r--src/tools/clippy/tests/ui/single_range_in_vec_init.stderr145
-rw-r--r--src/tools/clippy/tests/ui/skip_while_next.rs2
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.fixed1
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.rs1
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.stderr14
-rw-r--r--src/tools/clippy/tests/ui/starts_ends_with.fixed2
-rw-r--r--src/tools/clippy/tests/ui/starts_ends_with.rs2
-rw-r--r--src/tools/clippy/tests/ui/string_add.rs2
-rw-r--r--src/tools/clippy/tests/ui/string_lit_as_bytes.fixed2
-rw-r--r--src/tools/clippy/tests/ui/string_lit_as_bytes.rs2
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.rs16
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.stderr18
-rw-r--r--src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs1
-rw-r--r--src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr8
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed3
-rw-r--r--src/tools/clippy/tests/ui/swap.rs3
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr34
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.rs1
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.stderr2
-rw-r--r--src/tools/clippy/tests/ui/to_string_in_format_args_incremental.fixed9
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.fixed4
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.rs4
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs2
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs2
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed2
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs2
-rw-r--r--src/tools/clippy/tests/ui/try_err.fixed2
-rw-r--r--src/tools/clippy/tests/ui/try_err.rs2
-rw-r--r--src/tools/clippy/tests/ui/tuple_array_conversions.rs73
-rw-r--r--src/tools/clippy/tests/ui/tuple_array_conversions.stderr83
-rw-r--r--src/tools/clippy/tests/ui/type_complexity.rs2
-rw-r--r--src/tools/clippy/tests/ui/type_repetition_in_bounds.rs39
-rw-r--r--src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr18
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs24
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr26
-rw-r--r--src/tools/clippy/tests/ui/undropped_manually_drops.rs26
-rw-r--r--src/tools/clippy/tests/ui/undropped_manually_drops.stderr19
-rw-r--r--src/tools/clippy/tests/ui/unicode.fixed1
-rw-r--r--src/tools/clippy/tests/ui/unicode.rs1
-rw-r--r--src/tools/clippy/tests/ui/unicode.stderr20
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.fixed9
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.rs9
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.stderr142
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.rs2
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.rs3
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unit_return_expecting_ord.rs1
-rw-r--r--src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.rs2
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.stderr6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.fixed81
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.rs81
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.stderr110
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.fixed24
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.rs24
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.stderr56
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed30
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs30
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr102
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed89
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.rs89
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr521
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.rs118
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.stderr615
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_safety_comment.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_sort_by.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_sort_by.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed33
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs33
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs2
-rw-r--r--src/tools/clippy/tests/ui/unneeded_field_pattern.rs14
-rw-r--r--src/tools/clippy/tests/ui/unneeded_field_pattern.stderr4
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed9
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs9
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr30
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.rs8
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.stderr34
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.fixed7
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.rs7
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.stderr16
-rw-r--r--src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unseparated_prefix_literals.rs2
-rw-r--r--src/tools/clippy/tests/ui/unused_async.rs34
-rw-r--r--src/tools/clippy/tests/ui/unused_async.stderr25
-rw-r--r--src/tools/clippy/tests/ui/unwrap.rs1
-rw-r--r--src/tools/clippy/tests/ui/unwrap.stderr6
-rw-r--r--src/tools/clippy/tests/ui/unwrap_expect_used.rs1
-rw-r--r--src/tools/clippy/tests/ui/unwrap_expect_used.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.rs1
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.stderr4
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or_else_default.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or_else_default.rs2
-rwxr-xr-xsrc/tools/clippy/tests/ui/update-all-references.sh2
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed2
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/used_underscore_binding.rs2
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.fixed2
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.rs2
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.fixed45
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.rs45
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.stderr62
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion_try.rs1
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion_try.stderr18
-rw-r--r--src/tools/clippy/tests/ui/vec.fixed82
-rw-r--r--src/tools/clippy/tests/ui/vec.rs82
-rw-r--r--src/tools/clippy/tests/ui/vec.stderr56
-rw-r--r--src/tools/clippy/tests/ui/vtable_address_comparisons.rs12
-rw-r--r--src/tools/clippy/tests/ui/vtable_address_comparisons.stderr18
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.fixed4
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.rs4
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.stderr52
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.fixed4
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.rs4
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.stderr34
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.rs2
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.stderr32
-rw-r--r--src/tools/clippy/tests/ui/write_with_newline.fixed63
-rw-r--r--src/tools/clippy/tests/ui/write_with_newline.stderr12
-rw-r--r--src/tools/clippy/tests/workspace.rs40
-rw-r--r--src/tools/clippy/tests/workspace_test/Cargo.toml2
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/Cargo.toml10
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo.rs2
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo/hello.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/lib.rs5
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/bad/mod.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/main.rs13
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/foo.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/inner/mod.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/mod.rs2
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/Cargo.toml10
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/lib.rs7
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff.rs3
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff/most.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/mod.rs3
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/good.rs1
-rw-r--r--src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/main.rs9
-rw-r--r--src/tools/clippy/util/etc/vscode-tasks.json4
-rwxr-xr-xsrc/tools/clippy/util/versions.py25
-rw-r--r--src/tools/compiletest/src/common.rs120
-rw-r--r--src/tools/compiletest/src/header.rs48
-rw-r--r--src/tools/compiletest/src/header/needs.rs16
-rw-r--r--src/tools/compiletest/src/read2.rs2
-rw-r--r--src/tools/compiletest/src/runtest.rs436
-rw-r--r--src/tools/compiletest/src/util.rs2
-rw-r--r--src/tools/error_index_generator/main.rs5
-rw-r--r--src/tools/miropt-test-tools/src/lib.rs53
-rwxr-xr-xsrc/tools/publish_toolstate.py16
-rw-r--r--src/tools/rust-analyzer/.vscode/launch.json2
-rw-r--r--src/tools/rust-analyzer/Cargo.lock469
-rw-r--r--src/tools/rust-analyzer/Cargo.toml18
-rw-r--r--src/tools/rust-analyzer/bench_data/glorious_old_parser2
-rw-r--r--src/tools/rust-analyzer/crates/base-db/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/change.rs23
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs90
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs344
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs29
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs241
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs)170
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs400
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs1072
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs138
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/adt.rs)73
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/keys.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs211
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/expr.rs)132
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs)131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs98
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs293
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/layout.rs97
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs407
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lower.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs360
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs149
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs311
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs)85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs286
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs661
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs141
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs152
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs151
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs713
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs335
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/eager.rs240
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs214
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs102
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/quote.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs84
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs149
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs55
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs119
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs1435
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs377
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs115
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs51
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs671
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs542
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs931
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs64
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs461
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs218
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs127
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs157
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/interner.rs147
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs150
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs106
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs192
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs257
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs85
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs325
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs305
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs343
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs282
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs2209
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs792
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs676
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs1795
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs163
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs617
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs351
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs185
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs111
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs74
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs234
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs398
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs383
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tls.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs55
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs154
-rw-r--r--src/tools/rust-analyzer/crates/hir/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/db.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs796
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs66
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs94
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs297
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs538
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs161
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs248
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs209
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs185
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs58
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs133
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs125
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs722
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs43
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs351
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs119
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs67
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs110
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs132
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs161
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs149
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs56
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs291
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/assists.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/helpers.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs225
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/line_index.rs314
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs167
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/source_change.rs125
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs103
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt216
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt372
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/traits.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs175
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs474
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs145
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs232
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs91
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs177
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs152
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs85
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fixture.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs89
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs351
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs333
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs897
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs146
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs113
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs450
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs207
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs144
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/interpret_function.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs122
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs213
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/prime_caches.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs502
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs241
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs163
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html28
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html124
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs2
-rw-r--r--src/tools/rust-analyzer/crates/intern/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/lib.rs95
-rw-r--r--src/tools/rust-analyzer/crates/limit/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/benchmark.rs15
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander.rs5
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs34
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs128
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/lib.rs49
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/parser.rs36
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs74
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs15
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs91
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs26
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs103
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/types.rs24
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs56
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/shortcuts.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast18
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast24
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast26
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast24
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast120
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs8
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast39
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast38
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast58
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast102
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast58
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast175
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast269
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast76
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs2
-rw-r--r--src/tools/rust-analyzer/crates/paths/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/paths/src/lib.rs16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs37
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs10
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs60
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs28
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs38
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs106
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs156
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs510
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs32
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs89
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs451
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs304
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs81
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs83
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs332
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs166
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs1106
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs139
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs840
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs146
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs34
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs28
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs57
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs)81
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs)79
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs4
-rw-r--r--src/tools/rust-analyzer/crates/profile/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/profile/src/memory_usage.rs6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs29
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs92
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs39
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs64
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs1881
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs657
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt344
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt344
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt340
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt462
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json6420
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json12816
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml26
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs100
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs17
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs421
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs29
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs74
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs41
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs293
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs17
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs43
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs90
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs22
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs134
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs334
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs)237
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs71
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs38
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs655
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs319
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs59
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs33
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs200
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs89
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs32
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs2
-rw-r--r--src/tools/rust-analyzer/crates/sourcegen/src/lib.rs26
-rw-r--r--src/tools/rust-analyzer/crates/stdx/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/hash.rs80
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread.rs102
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs287
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs92
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs17
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs5
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs155
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs71
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/token_text.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs56
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs96
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs565
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/src/lib.rs52
-rw-r--r--src/tools/rust-analyzer/crates/tt/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/tt/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/vfs/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/file_set.rs4
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/lib.rs23
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs5
-rw-r--r--src/tools/rust-analyzer/docs/dev/lsp-extensions.md69
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc66
-rw-r--r--src/tools/rust-analyzer/docs/user/manual.adoc38
-rw-r--r--src/tools/rust-analyzer/lib/README.md7
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/lib.rs124
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/map.rs70
-rw-r--r--src/tools/rust-analyzer/lib/line-index/Cargo.toml11
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/lib.rs237
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/tests.rs11
-rw-r--r--src/tools/rust-analyzer/lib/line-index/tests/it.rs62
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/error.rs2
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/lib.rs70
-rw-r--r--src/tools/rust-installer/Cargo.toml5
-rwxr-xr-xsrc/tools/rust-installer/combine-installers.sh4
-rwxr-xr-xsrc/tools/rust-installer/gen-installer.sh4
-rw-r--r--src/tools/rust-installer/install-template.sh609
-rwxr-xr-xsrc/tools/rust-installer/make-tarballs.sh4
-rw-r--r--src/tools/rust-installer/src/combiner.rs55
-rw-r--r--src/tools/rust-installer/src/compression.rs2
-rw-r--r--src/tools/rust-installer/src/generator.rs58
-rw-r--r--src/tools/rust-installer/src/main.rs8
-rw-r--r--src/tools/rust-installer/src/scripter.rs23
-rw-r--r--src/tools/rust-installer/src/tarballer.rs25
-rw-r--r--src/tools/rust-installer/src/util.rs23
-rwxr-xr-xsrc/tools/rust-installer/test.sh1022
-rw-r--r--src/tools/rustdoc-gui-test/Cargo.toml1
-rw-r--r--src/tools/rustdoc-gui-test/src/main.rs53
-rw-r--r--src/tools/rustdoc-gui/tester.js4
-rw-r--r--src/tools/rustdoc-js/tester.js90
-rw-r--r--src/tools/rustfmt/.editorconfig3
-rw-r--r--src/tools/rustfmt/.github/workflows/upload-assets.yml5
-rw-r--r--src/tools/rustfmt/.travis.yml77
-rw-r--r--src/tools/rustfmt/CHANGELOG.md54
-rw-r--r--src/tools/rustfmt/Cargo.lock491
-rw-r--r--src/tools/rustfmt/Cargo.toml20
-rw-r--r--src/tools/rustfmt/Configurations.md97
-rw-r--r--src/tools/rustfmt/Contributing.md10
-rw-r--r--src/tools/rustfmt/README.md24
-rw-r--r--src/tools/rustfmt/config_proc_macro/Cargo.lock30
-rw-r--r--src/tools/rustfmt/config_proc_macro/Cargo.toml4
-rw-r--r--src/tools/rustfmt/config_proc_macro/src/attrs.rs18
-rw-r--r--src/tools/rustfmt/rust-toolchain2
-rw-r--r--src/tools/rustfmt/src/attr/doc_comment.rs7
-rw-r--r--src/tools/rustfmt/src/bin/main.rs2
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/main.rs13
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/test/targets.rs22
-rw-r--r--src/tools/rustfmt/src/comment.rs198
-rw-r--r--src/tools/rustfmt/src/config/config_type.rs10
-rw-r--r--src/tools/rustfmt/src/config/mod.rs7
-rw-r--r--src/tools/rustfmt/src/config/options.rs8
-rw-r--r--src/tools/rustfmt/src/expr.rs114
-rw-r--r--src/tools/rustfmt/src/formatting.rs17
-rw-r--r--src/tools/rustfmt/src/items.rs135
-rw-r--r--src/tools/rustfmt/src/lib.rs2
-rw-r--r--src/tools/rustfmt/src/macros.rs5
-rw-r--r--src/tools/rustfmt/src/overflow.rs4
-rw-r--r--src/tools/rustfmt/src/pairs.rs9
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs10
-rw-r--r--src/tools/rustfmt/src/parse/session.rs19
-rw-r--r--src/tools/rustfmt/src/types.rs76
-rw-r--r--src/tools/rustfmt/src/utils.rs1
-rw-r--r--src/tools/rustfmt/tests/rustfmt/main.rs12
-rw-r--r--src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/100.rs40
-rw-r--r--src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/50.rs40
-rw-r--r--src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/zero.rs40
-rw-r--r--src/tools/rustfmt/tests/source/configs/use_small_heuristics/default.rs10
-rw-r--r--src/tools/rustfmt/tests/source/configs/use_small_heuristics/max.rs10
-rw-r--r--src/tools/rustfmt/tests/source/configs/use_small_heuristics/off.rs10
-rw-r--r--src/tools/rustfmt/tests/source/issue-4041.rs5
-rw-r--r--src/tools/rustfmt/tests/source/issue-5234.rs51
-rw-r--r--src/tools/rustfmt/tests/source/issue-5488.rs17
-rw-r--r--src/tools/rustfmt/tests/source/issue-5586.rs164
-rw-r--r--src/tools/rustfmt/tests/source/issue_5686.rs40
-rw-r--r--src/tools/rustfmt/tests/source/itemized-blocks/no_wrap.rs36
-rw-r--r--src/tools/rustfmt/tests/source/itemized-blocks/wrap.rs36
-rw-r--r--src/tools/rustfmt/tests/source/let_else.rs161
-rw-r--r--src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/100.rs60
-rw-r--r--src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/50.rs62
-rw-r--r--src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/zero.rs66
-rw-r--r--src/tools/rustfmt/tests/target/configs/use_small_heuristics/default.rs12
-rw-r--r--src/tools/rustfmt/tests/target/configs/use_small_heuristics/max.rs10
-rw-r--r--src/tools/rustfmt/tests/target/configs/use_small_heuristics/off.rs16
-rw-r--r--src/tools/rustfmt/tests/target/doc-of-generic-item.rs18
-rw-r--r--src/tools/rustfmt/tests/target/issue-4041.rs6
-rw-r--r--src/tools/rustfmt/tests/target/issue-4210-disabled.rs15
-rw-r--r--src/tools/rustfmt/tests/target/issue-4210.rs15
-rw-r--r--src/tools/rustfmt/tests/target/issue-5234.rs47
-rw-r--r--src/tools/rustfmt/tests/target/issue-5488.rs17
-rw-r--r--src/tools/rustfmt/tests/target/issue-5586.rs177
-rw-r--r--src/tools/rustfmt/tests/target/issue_5686.rs42
-rw-r--r--src/tools/rustfmt/tests/target/issue_5691.rs16
-rw-r--r--src/tools/rustfmt/tests/target/issue_5728.rs5
-rw-r--r--src/tools/rustfmt/tests/target/issue_5729.rs5
-rw-r--r--src/tools/rustfmt/tests/target/itemized-blocks/no_wrap.rs36
-rw-r--r--src/tools/rustfmt/tests/target/itemized-blocks/wrap.rs62
-rw-r--r--src/tools/rustfmt/tests/target/let_else.rs253
-rw-r--r--src/tools/tidy/src/bins.rs26
-rw-r--r--src/tools/tidy/src/deps.rs43
-rw-r--r--src/tools/tidy/src/features.rs8
-rw-r--r--src/tools/tidy/src/main.rs6
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs7
-rw-r--r--src/tools/tidy/src/ui_tests.rs4
-rw-r--r--src/tools/tidy/src/walk.rs4
-rw-r--r--src/tools/x/Cargo.lock4
2022 files changed, 98758 insertions, 30876 deletions
diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs
index d2e9c324a..893195b69 100644
--- a/src/tools/build_helper/src/ci.rs
+++ b/src/tools/build_helper/src/ci.rs
@@ -4,8 +4,6 @@ use std::process::Command;
pub enum CiEnv {
/// Not a CI environment.
None,
- /// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds.
- AzurePipelines,
/// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
GitHubActions,
}
@@ -13,9 +11,7 @@ pub enum CiEnv {
impl CiEnv {
/// Obtains the current CI environment.
pub fn current() -> CiEnv {
- if std::env::var("TF_BUILD").map_or(false, |e| e == "True") {
- CiEnv::AzurePipelines
- } else if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
+ if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
CiEnv::GitHubActions
} else {
CiEnv::None
@@ -46,6 +42,8 @@ pub mod gha {
pub fn group(name: impl std::fmt::Display) -> Group {
if std::env::var_os("GITHUB_ACTIONS").is_some() {
eprintln!("::group::{name}");
+ } else {
+ eprintln!("{name}")
}
Group(())
}
diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs
index d3d2323db..3fa970373 100644
--- a/src/tools/build_helper/src/lib.rs
+++ b/src/tools/build_helper/src/lib.rs
@@ -1,2 +1,3 @@
pub mod ci;
pub mod git;
+pub mod util;
diff --git a/src/tools/build_helper/src/util.rs b/src/tools/build_helper/src/util.rs
new file mode 100644
index 000000000..11b8a228b
--- /dev/null
+++ b/src/tools/build_helper/src/util.rs
@@ -0,0 +1,45 @@
+use std::process::Command;
+
+/// Invokes `build_helper::util::detail_exit` with `cfg!(test)`
+#[macro_export]
+macro_rules! detail_exit_macro {
+ ($code:expr) => {
+ build_helper::util::detail_exit($code, cfg!(test));
+ };
+}
+
+/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
+/// If `is_test` true and code is an error code, it will cause a panic.
+pub fn detail_exit(code: i32, is_test: bool) -> ! {
+ // if in test and code is an error code, panic with status code provided
+ if is_test {
+ panic!("status code: {}", code);
+ } else {
+ // otherwise,exit with provided status code
+ std::process::exit(code);
+ }
+}
+
+pub fn fail(s: &str) -> ! {
+ eprintln!("\n\n{}\n\n", s);
+ detail_exit(1, cfg!(test));
+}
+
+pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
+ let status = match cmd.status() {
+ Ok(status) => status,
+ Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
+ };
+ if !status.success() {
+ if print_cmd_on_fail {
+ println!(
+ "\n\ncommand did not execute successfully: {:?}\n\
+ expected success, got: {}\n\n",
+ cmd, status
+ );
+ }
+ Err(())
+ } else {
+ Ok(())
+ }
+}
diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml
index 758b1b139..4680a7ab6 100644
--- a/src/tools/bump-stage0/Cargo.toml
+++ b/src/tools/bump-stage0/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.34"
curl = "0.4.38"
-indexmap = { version = "1.9.1", features = ["serde"] }
+indexmap = { version = "2.0.0", features = ["serde"] }
serde = { version = "1.0.125", features = ["derive"] }
serde_json = { version = "1.0.59", features = ["preserve_order"] }
toml = "0.5.7"
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml
index c522a19a1..3deae6355 100644
--- a/src/tools/cargo/.github/workflows/main.yml
+++ b/src/tools/cargo/.github/workflows/main.yml
@@ -12,9 +12,6 @@ defaults:
permissions:
contents: read
-env:
- CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
-
jobs:
# Check Code style quickly by running `rustfmt` over all code
rustfmt:
@@ -108,10 +105,6 @@ jobs:
- uses: actions/checkout@v3
- name: Dump Environment
run: ci/dump-environment.sh
- - name: Update Rustup (temporary workaround)
- run: rustup self update
- shell: bash
- if: startsWith(matrix.os, 'windows')
- run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
- run: rustup target add ${{ matrix.other }}
- run: rustup component add rustc-dev llvm-tools-preview rust-docs
@@ -225,6 +218,7 @@ jobs:
name: bors build finished
needs:
- build_std
+ - clippy
- docs
- lockfile
- resolver
@@ -241,6 +235,7 @@ jobs:
name: bors build finished
needs:
- build_std
+ - clippy
- docs
- lockfile
- resolver
diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md
index 5cb4d11c5..0784b2638 100644
--- a/src/tools/cargo/CHANGELOG.md
+++ b/src/tools/cargo/CHANGELOG.md
@@ -1,10 +1,104 @@
# Changelog
+## Cargo 1.72 (2023-08-24)
+[64fb38c9...HEAD](https://github.com/rust-lang/cargo/compare/64fb38c9...HEAD)
+
+### Added
+
+- Add support of the "default" keyword to reset previously set `build.jobs`
+ parallelism back to the default.
+ [#12222](https://github.com/rust-lang/cargo/pull/12222)
+
+### Changed
+
+- Cargo now warns when an edition 2021 package is in a virtual workspace and
+ `workspace.resolver` is not set. It is recommended to set the resolver
+ version for workspaces explicitly.
+ [#10910](https://github.com/rust-lang/cargo/pull/10910)
+- Set IBM AIX shared libraries search path to `LIBPATH`.
+ [#11968](https://github.com/rust-lang/cargo/pull/11968)
+- Don't pass `-C debuginfo=0` to rustc as it is the default value.
+ [#12022](https://github.com/rust-lang/cargo/pull/12022)
+ [#12205](https://github.com/rust-lang/cargo/pull/12205)
+- Added a message on reusing previous temporary path on `cargo install` failures.
+ [#12231](https://github.com/rust-lang/cargo/pull/12231)
+- Added a message when `rustup` override shorthand is put in a wrong position.
+ [#12226](https://github.com/rust-lang/cargo/pull/12226)
+
+### Fixed
+
+- `cargo clean` uses `remove_dir_all` as a fallback to resolve race conditions.
+ [#11442](https://github.com/rust-lang/cargo/pull/11442)
+- Reduced the chance Cargo re-formats the user's `[features]` table.
+ [#12191](https://github.com/rust-lang/cargo/pull/12191)
+- Fixed nested Git submodules not able to fetch.
+ [#12244](https://github.com/rust-lang/cargo/pull/12244)
+
+### Nightly only
+
+- 🔥 The `-Zscript` is an experimental feature to add unstable support for
+ single-file packages in Cargo, so we can explore the design and resolve
+ questions with an implementation to collect feedback on.
+ ([eRFC 3424](https://github.com/rust-lang/rfcs/blob/master/text/3424-cargo-script.md))
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#script)
+ [#12245](https://github.com/rust-lang/cargo/pull/12245)
+- Automatically inherit workspace lints when running `cargo new`/`cargo init`.
+ [#12174](https://github.com/rust-lang/cargo/pull/12174)
+
+### Documentation
+
+- Added a description of `Cargo.lock` conflicts in the Cargo FAQ.
+ [#12185](https://github.com/rust-lang/cargo/pull/12185)
+- Added a small note about indexes ignoring SemVer build metadata.
+ [#12206](https://github.com/rust-lang/cargo/pull/12206)
+- Added doc comments for types and friends in `cargo::sources` module.
+ [#12192](https://github.com/rust-lang/cargo/pull/12192)
+ [#12239](https://github.com/rust-lang/cargo/pull/12239)
+ [#12247](https://github.com/rust-lang/cargo/pull/12247)
+
+### Internal
+
+- Updated to `gix` 0.45 for multi-round pack negotiations.
+ [#12236](https://github.com/rust-lang/cargo/pull/12236)
+- Updated to `curl-sys` 0.4.63, which corresponds to curl 8.1.2.
+ [#12218](https://github.com/rust-lang/cargo/pull/12218)
+- Removed unused features from `windows-sys` dependency.
+ [#12176](https://github.com/rust-lang/cargo/pull/12176)
+- Refactored compiler invocations
+ [#12211](https://github.com/rust-lang/cargo/pull/12211)
+- Refactored git and registry sources, and registry data.
+ [#12203](https://github.com/rust-lang/cargo/pull/12203)
+ [#12197](https://github.com/rust-lang/cargo/pull/12197)
+ [#12240](https://github.com/rust-lang/cargo/pull/12240)
+ [#12248](https://github.com/rust-lang/cargo/pull/12248)
+- Lexicographically order `-Z` flags.
+ [#12182](https://github.com/rust-lang/cargo/pull/12182)
+ [#12223](https://github.com/rust-lang/cargo/pull/12223)
+ [#12224](https://github.com/rust-lang/cargo/pull/12224)
+- Several Cargo's own test infra improvements and speed-ups.
+ [#12184](https://github.com/rust-lang/cargo/pull/12184)
+ [#12188](https://github.com/rust-lang/cargo/pull/12188)
+ [#12189](https://github.com/rust-lang/cargo/pull/12189)
+ [#12194](https://github.com/rust-lang/cargo/pull/12194)
+ [#12199](https://github.com/rust-lang/cargo/pull/12199)
+- Migrated print-ban from test to clippy
+ [#12246](https://github.com/rust-lang/cargo/pull/12246)
+
## Cargo 1.71 (2023-07-13)
-[84b7041f...HEAD](https://github.com/rust-lang/cargo/compare/84b7041f...HEAD)
+[84b7041f...rust-1.71.0](https://github.com/rust-lang/cargo/compare/84b7041f...rust-1.71.0)
### Added
+- Allowed named debuginfo options in Cargo.toml.
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#debug)
+ [#11958](https://github.com/rust-lang/cargo/pull/11958)
+- Added `workspace_default_members` to the output of `cargo metadata`.
+ [#11978](https://github.com/rust-lang/cargo/pull/11978)
+- `cargo add` now considers `rust-version` when selecting packages.
+ [#12078](https://github.com/rust-lang/cargo/pull/12078)
+- Automatically inherit workspace fields when running `cargo new`/`cargo init`.
+ [#12069](https://github.com/rust-lang/cargo/pull/12069)
+
### Changed
- ❗ Optimized the usage under `rustup`. When Cargo detects it will run `rustc`
@@ -23,39 +117,151 @@
[#12107](https://github.com/rust-lang/cargo/pull/12107)
- Better error message when getting an empty dependency table in Cargo.toml.
[#11997](https://github.com/rust-lang/cargo/pull/11997)
-- Use restricted Damerau-Levenshtein algorithm to provide typo suggestions.
- [#11963](https://github.com/rust-lang/cargo/pull/11963)
+- Better error message when empty dependency was specified in Cargo.toml.
+ [#12001](https://github.com/rust-lang/cargo/pull/12001)
+- `--help` text is now wrapping for readability on narrow screens.
+ [#12013](https://github.com/rust-lang/cargo/pull/12013)
+- Tweaked the order of arguments in `--help` text to clarify role of `--bin`.
+ [#12157](https://github.com/rust-lang/cargo/pull/12157)
+- `rust-version` is included in `cargo publish` requests to registries.
+ [#12041](https://github.com/rust-lang/cargo/pull/12041)
### Fixed
- Corrected the bug report URL for `cargo clippy --fix`.
[#11882](https://github.com/rust-lang/cargo/pull/11882)
+- Cargo now applies `[env]` to rust invocations for target info discovery.
+ [#12029](https://github.com/rust-lang/cargo/pull/12029)
+- Fixed tokens not redacted in http debug when using HTTP/2.
+ [#12095](https://github.com/rust-lang/cargo/pull/12095)
+- Fixed `-C debuginfo` not passed in some situation, leading to build cache miss.
+ [#12165](https://github.com/rust-lang/cargo/pull/12165)
+- Fixed the ambiguity when `cargo install` found packages with the same name.
+ The ambiguity happened in a situation like a package depending on old versions
+ of itself.
+ [#12015](https://github.com/rust-lang/cargo/pull/12015)
+- Fixed a false positive that `cargo package` checks for conflict files.
+ [#12135](https://github.com/rust-lang/cargo/pull/12135)
+- Fixed `dep/feat` syntax not working when co-exist with `dep:` syntax, and
+ trying to enable features of an optional dependency.
+ [#12130](https://github.com/rust-lang/cargo/pull/12130)
+- Fixed `cargo tree` not handling the output with `-e no-proc-macro` correctly.
+ [#12044](https://github.com/rust-lang/cargo/pull/12044)
+- Warn instead of error in `cargo package` on empty `readme` or `license-file`
+ in Cargo.toml.
+ [#12036](https://github.com/rust-lang/cargo/pull/12036)
+- Fixed when an HTTP proxy is in use and the Cargo executable links to a
+ certain version of system libcurl, CURL connections might fail. Affected
+ libcurl versions: 7.87.0, 7.88.0, 7.88.1.
+ [#12234](https://github.com/rust-lang/cargo/pull/12234)
+ [#12242](https://github.com/rust-lang/cargo/pull/12242)
### Nightly only
+- 🔥 The `-Zgitoxide` feature now supports shallow clones and fetches for
+ dependencies and registry indexes.
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gitoxide)
+ [#11840](https://github.com/rust-lang/cargo/pull/11840)
+- 🔥 The `-Zlints` feature enables configuring lints rules in Cargo.toml
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lints)
+ [#12148](https://github.com/rust-lang/cargo/pull/12148)
+ [#12168](https://github.com/rust-lang/cargo/pull/12168)
- The `-Zbuild-std` breakage of missing features in `nightly-2023-05-04` has
been fixed in `nightly-2023-05-05`.
[#12088](https://github.com/rust-lang/cargo/pull/12088)
- Recompile on profile rustflags changes.
[#11981](https://github.com/rust-lang/cargo/pull/11981)
+- Added `-Zmsrv-policy` feature flag placeholder.
+ [#12043](https://github.com/rust-lang/cargo/pull/12043)
### Documentation
+- Added Cargo team charter.
+ [docs](https://doc.crates.io/contrib/team.html)
+ [#12010](https://github.com/rust-lang/cargo/pull/12010)
+- SemVer: Adding `#[non_exhaustive]` on existing items is a breaking change.
+ [#10877](https://github.com/rust-lang/cargo/pull/10877)
+- SemVer: It is not a breaking change to make an unsafe function safe.
+ [#12116](https://github.com/rust-lang/cargo/pull/12116)
+- SemVer: changeing MSRV is generally a minor change.
+ [#12122](https://github.com/rust-lang/cargo/pull/12122)
+- Clarify when and how to `cargo yank`.
+ [#11862](https://github.com/rust-lang/cargo/pull/11862)
+- Clarify that crates.io doesn't link to docs.rs right away.
+ [#12146](https://github.com/rust-lang/cargo/pull/12146)
+- Clarify documentation around test target setting.
+ [#12032](https://github.com/rust-lang/cargo/pull/12032)
+- Specify `rust_version` in Index format.
+ [#12040](https://github.com/rust-lang/cargo/pull/12040)
+- Specify `msg` in owner-remove registry API response.
+ [#12068](https://github.com/rust-lang/cargo/pull/12068)
+- Added more documentation for artifact-dependencies.
+ [#12110](https://github.com/rust-lang/cargo/pull/12110)
+- Added doc comments for `Source` and build script for cargo-the-library.
+ [#12133](https://github.com/rust-lang/cargo/pull/12133)
+ [#12153](https://github.com/rust-lang/cargo/pull/12153)
+ [#12159](https://github.com/rust-lang/cargo/pull/12159)
+- Several typo and broken link fixes.
+ [#12018](https://github.com/rust-lang/cargo/pull/12018)
+ [#12020](https://github.com/rust-lang/cargo/pull/12020)
+ [#12049](https://github.com/rust-lang/cargo/pull/12049)
+ [#12067](https://github.com/rust-lang/cargo/pull/12067)
+ [#12073](https://github.com/rust-lang/cargo/pull/12073)
+ [#12143](https://github.com/rust-lang/cargo/pull/12143)
+- home: clarify the behavior on each platform
+ [#12047](https://github.com/rust-lang/cargo/pull/12047)
+
### Internal
-- Cargo is now a Cargo workspace. We dogfood ourselves finally.
+- Updated to `linux-raw-sys` 0.3.2
+ [#11998](https://github.com/rust-lang/cargo/pull/11998)
+- Updated to `git2` 0.17.1, which corresponds to libgit2 1.6.4.
+ [#12096](https://github.com/rust-lang/cargo/pull/12096)
+- Updated to `windows-sys` 0.48.0
+ [#12021](https://github.com/rust-lang/cargo/pull/12021)
+- Updated to `libc` 0.2.144
+ [#12014](https://github.com/rust-lang/cargo/pull/12014)
+ [#12098](https://github.com/rust-lang/cargo/pull/12098)
+- Updated to `openssl-src` 111.25.3+1.1.1t
+ [#12005](https://github.com/rust-lang/cargo/pull/12005)
+- Updated to `home` 0.5.5
+ [#12037](https://github.com/rust-lang/cargo/pull/12037)
+- Enabled feature `Win32_System_Console` feature since it is used.
+ [#12016](https://github.com/rust-lang/cargo/pull/12016)
+- Cargo is now a Cargo workspace. We dogfood ourselves finally!
[#11851](https://github.com/rust-lang/cargo/pull/11851)
[#11994](https://github.com/rust-lang/cargo/pull/11994)
[#11996](https://github.com/rust-lang/cargo/pull/11996)
-- Allow win/mac credential managers to build on all platforms.
- [#11993](https://github.com/rust-lang/cargo/pull/11993)
-- Use `openssl` only on non-Windows platforms.
- [#11979](https://github.com/rust-lang/cargo/pull/11979)
-- A new, straightforward issue labels system for Cargo contributors.
+ [#12024](https://github.com/rust-lang/cargo/pull/12024)
+ [#12025](https://github.com/rust-lang/cargo/pull/12025)
+ [#12057](https://github.com/rust-lang/cargo/pull/12057)
+- 🔥 A new, straightforward issue labels system for Cargo contributors.
[docs](https://doc.crates.io/contrib/issues.html)
[#11995](https://github.com/rust-lang/cargo/pull/11995)
[#12002](https://github.com/rust-lang/cargo/pull/12002)
[#12003](https://github.com/rust-lang/cargo/pull/12003)
+- Allow win/mac credential managers to build on all platforms.
+ [#11993](https://github.com/rust-lang/cargo/pull/11993)
+ [#12027](https://github.com/rust-lang/cargo/pull/12027)
+- Use `openssl` only on non-Windows platforms.
+ [#11979](https://github.com/rust-lang/cargo/pull/11979)
+- Use restricted Damerau-Levenshtein algorithm to provide typo suggestions.
+ [#11963](https://github.com/rust-lang/cargo/pull/11963)
+- Added a new xtask `cargo build-man`.
+ [#12048](https://github.com/rust-lang/cargo/pull/12048)
+- Added a new xtask `cargo stale-label`.
+ [#12051](https://github.com/rust-lang/cargo/pull/12051)
+- Added a new xtask `cargo unpublished`.
+ [#12039](https://github.com/rust-lang/cargo/pull/12039)
+ [#12045](https://github.com/rust-lang/cargo/pull/12045)
+ [#12085](https://github.com/rust-lang/cargo/pull/12085)
+- CI: check if any version bump needed for member crates.
+ [#12126](https://github.com/rust-lang/cargo/pull/12126)
+- Fixed some test infra issues.
+ [#11976](https://github.com/rust-lang/cargo/pull/11976)
+ [#12026](https://github.com/rust-lang/cargo/pull/12026)
+ [#12055](https://github.com/rust-lang/cargo/pull/12055)
+ [#12117](https://github.com/rust-lang/cargo/pull/12117)
## Cargo 1.70 (2023-06-01)
[9880b408...rust-1.70.0](https://github.com/rust-lang/cargo/compare/9880b408...rust-1.70.0)
@@ -90,7 +296,7 @@
[#11878](https://github.com/rust-lang/cargo/pull/11878)
- Added delays to network retries in Cargo.
[#11881](https://github.com/rust-lang/cargo/pull/11881)
-- Refined `cargo puslish` message when waiting for a publish complete.
+- Refined `cargo publish` message when waiting for a publish complete.
[#11713](https://github.com/rust-lang/cargo/pull/11713)
- Better error message when `cargo install` from a git repository but found
multiple packages.
@@ -392,7 +598,7 @@
for each revision from the same git repository.
[#10690](https://github.com/rust-lang/cargo/pull/1090)
- Cargo contributors can relabel issues via triagebot.
- [doc](https://github.com/rust-lang/triagebot/wiki/Labeling)
+ [doc](https://forge.rust-lang.org/triagebot/labeling.html)
[#11498](https://github.com/rust-lang/cargo/pull/11498)
- Cargo contributors can write tests in containers.
[#11583](https://github.com/rust-lang/cargo/pull/11583)
diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock
index 14fd1d056..fe365bbcb 100644
--- a/src/tools/cargo/Cargo.lock
+++ b/src/tools/cargo/Cargo.lock
@@ -30,50 +30,68 @@ dependencies = [
]
[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "anstream"
-version = "0.2.6"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
+ "anstyle-query",
"anstyle-wincon",
- "concolor-override",
- "concolor-query",
+ "colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "0.3.5"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "anstyle-parse"
-version = "0.1.1"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
+checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
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 0.48.0",
+]
+
+[[package]]
name = "anstyle-wincon"
-version = "0.2.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
]
[[package]]
name = "anyhow"
-version = "1.0.70"
+version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "arc-swap"
@@ -112,9 +130,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
-version = "0.21.0"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
+checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64ct"
@@ -156,9 +174,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.2.1"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813"
+checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
[[package]]
name = "bitmaps"
@@ -180,9 +198,9 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
+checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
dependencies = [
"memchr",
"once_cell",
@@ -201,9 +219,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.12.0"
+version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "byteorder"
@@ -244,7 +262,7 @@ dependencies = [
[[package]]
name = "cargo"
-version = "0.72.1"
+version = "0.73.0"
dependencies = [
"anyhow",
"base64",
@@ -253,7 +271,7 @@ dependencies = [
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
- "clap 4.2.1",
+ "clap 4.3.3",
"crates-io",
"curl",
"curl-sys",
@@ -274,10 +292,8 @@ dependencies = [
"ignore",
"im-rc",
"indexmap",
- "is-terminal",
"itertools",
"jobserver",
- "lazy_static",
"lazycell",
"libc",
"libgit2-sys",
@@ -289,6 +305,7 @@ dependencies = [
"pasetors",
"pathdiff",
"pretty_env_logger",
+ "pulldown-cmark",
"rand",
"rustfix",
"same-file",
@@ -301,6 +318,7 @@ dependencies = [
"shell-escape",
"snapbox",
"strip-ansi-escapes",
+ "syn 2.0.18",
"tar",
"tempfile",
"termcolor",
@@ -399,7 +417,7 @@ dependencies = [
[[package]]
name = "cargo-util"
-version = "0.2.4"
+version = "0.2.5"
dependencies = [
"anyhow",
"core-foundation",
@@ -464,18 +482,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.2.1"
+version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
+checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.2.1"
+version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
+checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab"
dependencies = [
"anstream",
"anstyle",
@@ -487,9 +505,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.4.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "clru"
@@ -498,19 +516,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
[[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 = "const-oid"
@@ -545,9 +554,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "cpufeatures"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
+checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
dependencies = [
"libc",
]
@@ -632,9 +641,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.14"
+version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
@@ -645,18 +654,18 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.15"
+version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-bigint"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7"
+checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
dependencies = [
"generic-array",
"rand_core",
@@ -676,9 +685,9 @@ dependencies = [
[[package]]
name = "csv"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
+checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
dependencies = [
"csv-core",
"itoa 1.0.6",
@@ -728,9 +737,9 @@ dependencies = [
[[package]]
name = "curl-sys"
-version = "0.4.61+curl-8.0.1"
+version = "0.4.63+curl-8.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79"
+checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
dependencies = [
"cc",
"libc",
@@ -744,9 +753,9 @@ dependencies = [
[[package]]
name = "der"
-version = "0.7.3"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed"
+checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17"
dependencies = [
"const-oid",
"pem-rfc7468",
@@ -761,9 +770,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
-version = "0.10.6"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
@@ -773,21 +782,22 @@ dependencies = [
[[package]]
name = "dunce"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
+checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "ecdsa"
-version = "0.16.6"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd"
+checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428"
dependencies = [
"der",
"digest",
"elliptic-curve",
"rfc6979",
"signature",
+ "spki",
]
[[package]]
@@ -807,9 +817,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "elliptic-curve"
-version = "0.13.4"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7"
+checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -912,9 +922,9 @@ dependencies = [
[[package]]
name = "flate2"
-version = "1.0.25"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
+checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
dependencies = [
"crc32fast",
"libz-sys",
@@ -944,9 +954,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"percent-encoding",
]
@@ -974,9 +984,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"js-sys",
@@ -987,9 +997,9 @@ dependencies = [
[[package]]
name = "git2"
-version = "0.17.1"
+version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b7905cdfe33d31a88bb2e8419ddd054451f5432d1da9eaf2ac7804ee1ea12d5"
+checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044"
dependencies = [
"bitflags 1.3.2",
"libc",
@@ -1014,12 +1024,13 @@ dependencies = [
[[package]]
name = "gix"
-version = "0.44.1"
+version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bf41b61f7df395284f7a579c0fa1a7e012c5aede655174d4e91299ef1cac643"
+checksum = "bf2a03ec66ee24d1b2bae3ab718f8d14f141613810cb7ff6756f7db667f1cd82"
dependencies = [
"gix-actor",
"gix-attributes",
+ "gix-commitgraph",
"gix-config",
"gix-credentials",
"gix-date",
@@ -1034,6 +1045,7 @@ dependencies = [
"gix-index",
"gix-lock",
"gix-mailmap",
+ "gix-negotiate",
"gix-object",
"gix-odb",
"gix-pack",
@@ -1062,9 +1074,9 @@ dependencies = [
[[package]]
name = "gix-actor"
-version = "0.20.0"
+version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "848efa0f1210cea8638f95691c82a46f98a74b9e3524f01d4955ebc25a8f84f3"
+checksum = "9fe73f9f6be1afbf1bd5be919a9636fa560e2f14d42262a934423ed6760cd838"
dependencies = [
"bstr",
"btoi",
@@ -1076,9 +1088,9 @@ dependencies = [
[[package]]
name = "gix-attributes"
-version = "0.12.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3015baa01ad2122fbcaab7863c857a603eb7b7ec12ac8141207c42c6439805e2"
+checksum = "78b79590ac382f80d87e06416f5fcac6fee5d83dcb152a00ed0bdbaa988acc31"
dependencies = [
"bstr",
"gix-glob",
@@ -1093,36 +1105,50 @@ dependencies = [
[[package]]
name = "gix-bitmap"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55a95f4942360766c3880bdb2b4b57f1ef73b190fc424755e7fdf480430af618"
+checksum = "fc02feb20ad313d52a450852f2005c2205d24f851e74d82b7807cbe12c371667"
dependencies = [
"thiserror",
]
[[package]]
name = "gix-chunk"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0d39583cab06464b8bf73b3f1707458270f0e7383cb24c3c9c1a16e6f792978"
+checksum = "a7acf3bc6c4b91e8fb260086daf5e105ea3a6d913f5fd3318137f7e309d6e540"
dependencies = [
"thiserror",
]
[[package]]
name = "gix-command"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab"
+checksum = "5f6141b70cfb21255223e42f3379855037cbbe8673b58dd8318d2f09b516fad1"
dependencies = [
"bstr",
]
[[package]]
+name = "gix-commitgraph"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8490ae1b3d55c47e6a71d247c082304a2f79f8d0332c1a2f5693d42a2021a09"
+dependencies = [
+ "bstr",
+ "gix-chunk",
+ "gix-features",
+ "gix-hash",
+ "memmap2",
+ "thiserror",
+]
+
+[[package]]
name = "gix-config"
-version = "0.22.0"
+version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d252a0eddb6df74600d3d8872dc9fe98835a7da43110411d705b682f49d4ac1"
+checksum = "51f310120ae1ba8f0ca52fb22876ce9bad5b15c8ffb3eb7302e4b64a3b9f681c"
dependencies = [
"bstr",
"gix-config-value",
@@ -1142,11 +1168,11 @@ dependencies = [
[[package]]
name = "gix-config-value"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "786861e84a5793ad5f863d846de5eb064cd23b87e61ad708c8c402608202e7be"
+checksum = "6f216df1c33e6e1555923eff0096858a879e8aaadd35b5d788641e4e8064c892"
dependencies = [
- "bitflags 2.2.1",
+ "bitflags 2.3.2",
"bstr",
"gix-path",
"libc",
@@ -1155,9 +1181,9 @@ dependencies = [
[[package]]
name = "gix-credentials"
-version = "0.14.0"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4874a4fc11ffa844a3c2b87a66957bda30a73b577ef1acf15ac34df5745de5ff"
+checksum = "c6f89fea8acd28f5ef8fa5042146f1637afd4d834bc8f13439d8fd1e5aca0d65"
dependencies = [
"bstr",
"gix-command",
@@ -1171,9 +1197,9 @@ dependencies = [
[[package]]
name = "gix-date"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99056f37270715f5c7584fd8b46899a2296af9cae92463bf58b8bd1f5a78e553"
+checksum = "bc164145670e9130a60a21670d9b6f0f4f8de04e5dd256c51fa5a0340c625902"
dependencies = [
"bstr",
"itoa 1.0.6",
@@ -1183,9 +1209,9 @@ dependencies = [
[[package]]
name = "gix-diff"
-version = "0.29.0"
+version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "644a0f2768bc42d7a69289ada80c9e15c589caefc6a315d2307202df83ed1186"
+checksum = "9029ad0083cc286a4bd2f5b3bf66bb66398abc26f2731a2824cd5edfc41a0e33"
dependencies = [
"gix-hash",
"gix-object",
@@ -1195,9 +1221,9 @@ dependencies = [
[[package]]
name = "gix-discover"
-version = "0.18.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5012710ebdecf6193c6866d6409a3b702a4aa0d78c605bc343590b44ab9962a1"
+checksum = "aba9c6c0d1f2b2efe65581de73de4305004612d49c83773e783202a7ef204f46"
dependencies = [
"bstr",
"dunce",
@@ -1210,9 +1236,9 @@ dependencies = [
[[package]]
name = "gix-features"
-version = "0.29.0"
+version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf69b0f5c701cc3ae22d3204b671907668f6437ca88862d355eaf9bc47a4f897"
+checksum = "3a8c493409bf6060d408eec9bbdd1b12ea351266b50012e2a522f75dfc7b8314"
dependencies = [
"bytes",
"crc32fast",
@@ -1230,20 +1256,20 @@ dependencies = [
[[package]]
name = "gix-fs"
-version = "0.1.1"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b37a1832f691fdc09910bd267f9a2e413737c1f9ec68c6e31f9e802616278a9"
+checksum = "30da8997008adb87f94e15beb7ee229f8a48e97af585a584bfee4a5a1880aab5"
dependencies = [
"gix-features",
]
[[package]]
name = "gix-glob"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c07c98204529ac3f24b34754540a852593d2a4c7349008df389240266627a72a"
+checksum = "cd0ade1e80ab1f079703d1824e1daf73009096386aa7fd2f0477f6e4ac0a558e"
dependencies = [
- "bitflags 2.2.1",
+ "bitflags 2.3.2",
"bstr",
"gix-features",
"gix-path",
@@ -1251,9 +1277,9 @@ dependencies = [
[[package]]
name = "gix-hash"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078eec3ac2808cc03f0bddd2704cb661da5c5dc33b41a9d7947b141d499c7c42"
+checksum = "ee181c85d3955f54c4426e6bfaeeada4428692e1a39b8788c2ac7785fc301dd8"
dependencies = [
"hex",
"thiserror",
@@ -1261,9 +1287,9 @@ dependencies = [
[[package]]
name = "gix-hashtable"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afebb85691c6a085b114e01a27f4a61364519298c5826cb87a45c304802299bc"
+checksum = "bd259bd0d96e6153e357a8cdaca76c48e103fd34208b6c0ce77b1ad995834bd2"
dependencies = [
"gix-hash",
"hashbrown 0.13.2",
@@ -1272,9 +1298,9 @@ dependencies = [
[[package]]
name = "gix-ignore"
-version = "0.2.0"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba205b6df563e2906768bb22834c82eb46c5fdfcd86ba2c347270bc8309a05b2"
+checksum = "fc6f7f101a0ccce808dbf7008ba131dede94e20257e7bde7a44cbb2f8c775625"
dependencies = [
"bstr",
"gix-glob",
@@ -1284,11 +1310,11 @@ dependencies = [
[[package]]
name = "gix-index"
-version = "0.16.0"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa282756760f79c401d4f4f42588fbb4aa27bbb4b0830f3b4d3480c21a4ac5a7"
+checksum = "616ba958fabfb11263fa042c35690d48a6c7be4e9277e2c7e24ff263b3fe7b82"
dependencies = [
- "bitflags 2.2.1",
+ "bitflags 2.3.2",
"bstr",
"btoi",
"filetime",
@@ -1306,20 +1332,20 @@ dependencies = [
[[package]]
name = "gix-lock"
-version = "5.0.0"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41b80172055c5d8017a48ddac5cc7a95421c00211047db0165c97853c4f05194"
+checksum = "3ec5d5e6f07316d3553aa7425e3ecd935ec29882556021fe1696297a448af8d2"
dependencies = [
- "fastrand",
"gix-tempfile",
+ "gix-utils",
"thiserror",
]
[[package]]
name = "gix-mailmap"
-version = "0.12.0"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8856cec3bdc3610c06970d28b6cb20a0c6621621cf9a8ec48cbd23f2630f362"
+checksum = "4653701922c920e009f1bc4309feaff14882ade017770788f9a150928da3fa6a"
dependencies = [
"bstr",
"gix-actor",
@@ -1327,10 +1353,25 @@ dependencies = [
]
[[package]]
+name = "gix-negotiate"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "945c3ef1e912e44a5f405fc9e924edf42000566a1b257ed52cb1293300f6f08c"
+dependencies = [
+ "bitflags 2.3.2",
+ "gix-commitgraph",
+ "gix-hash",
+ "gix-object",
+ "gix-revision",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
name = "gix-object"
-version = "0.29.1"
+version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9bb30ce0818d37096daa29efe361a4bc6dd0b51a5726598898be7e9a40a01e1"
+checksum = "8926c8f51c44dec3e709cb5dbc93deb9e8d4064c43c9efc54c158dcdfe8446c7"
dependencies = [
"bstr",
"btoi",
@@ -1347,9 +1388,9 @@ dependencies = [
[[package]]
name = "gix-odb"
-version = "0.45.0"
+version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2f324aa67672b6d0f2c0fa93f96eb6a7029d260e4c1df5dce3c015f5e5add"
+checksum = "4b234d806278eeac2f907c8b5a105c4ba537230c1a9d9236d822bf0db291f8f3"
dependencies = [
"arc-swap",
"gix-features",
@@ -1365,9 +1406,9 @@ dependencies = [
[[package]]
name = "gix-pack"
-version = "0.35.0"
+version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "164a515900a83257ae4aa80e741655bee7a2e39113fb535d7a5ac623b445ff20"
+checksum = "7d2a14cb3156037eedb17d6cb7209b7180522b8949b21fd0fe3184c0a1d0af88"
dependencies = [
"clru",
"gix-chunk",
@@ -1387,9 +1428,9 @@ dependencies = [
[[package]]
name = "gix-packetline"
-version = "0.16.0"
+version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51f53abaf1171d2fe99f80ac8ed6645904a1bfd706674749ac112bdd2d4f0777"
+checksum = "74414f89a6b72fa1a530ce8e646faf1a05499c3f4a5c15441d17ae8c978578eb"
dependencies = [
"bstr",
"hex",
@@ -1398,9 +1439,9 @@ dependencies = [
[[package]]
name = "gix-path"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fc78f47095a0c15aea0e66103838f0748f4494bf7a9555dfe0f00425400396c"
+checksum = "c1226f2e50adeb4d76c754c1856c06f13a24cad1624801653fbf09b869e5b808"
dependencies = [
"bstr",
"home 0.5.5",
@@ -1410,9 +1451,9 @@ dependencies = [
[[package]]
name = "gix-prompt"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "330d11fdf88fff3366c2491efde2f3e454958efe7d5ddf60272e8fb1d944bb01"
+checksum = "e15fe57fa48572b7d3bf465d6a2a0351cd3c55cba74fd5f0b9c23689f9c1a31e"
dependencies = [
"gix-command",
"gix-config-value",
@@ -1423,9 +1464,9 @@ dependencies = [
[[package]]
name = "gix-protocol"
-version = "0.32.0"
+version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877e49417f1730f4dbc2f7d9a2ab0f8b2f49ef08f97270691403ecde3d961e3a"
+checksum = "92a17058b45c461f0847528c5fb6ee6e76115e026979eb2d2202f98ee94f6c24"
dependencies = [
"bstr",
"btoi",
@@ -1440,9 +1481,9 @@ dependencies = [
[[package]]
name = "gix-quote"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb"
+checksum = "29d59489bff95b06dcdabe763b7266d3dc0a628cac1ac1caf65a7ca0a43eeae0"
dependencies = [
"bstr",
"btoi",
@@ -1451,9 +1492,9 @@ dependencies = [
[[package]]
name = "gix-ref"
-version = "0.29.0"
+version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8212ecfe41815a2f1b059d82171d6276758cfac5506a5e0f04ad45ef0b1924a"
+checksum = "ebdd999256f4ce8a5eefa89999879c159c263f3493a951d62aa5ce42c0397e1c"
dependencies = [
"gix-actor",
"gix-features",
@@ -1471,9 +1512,9 @@ dependencies = [
[[package]]
name = "gix-refspec"
-version = "0.10.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6ea733820df67e4cd7797deb12727905824d8f5b7c59d943c456d314475892"
+checksum = "72bfd622abc86dd8ad1ec51b9eb77b4f1a766b94e3a1b87cf4a022c5b5570cf4"
dependencies = [
"bstr",
"gix-hash",
@@ -1485,25 +1526,40 @@ dependencies = [
[[package]]
name = "gix-revision"
-version = "0.13.0"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "810f35e9afeccca999d5d348b239f9c162353127d2e13ff3240e31b919e35476"
+checksum = "5044f56cd7a487ce9b034cbe0252ae0b6b47ff56ca3dabd79bc30214d0932cd7"
dependencies = [
"bstr",
"gix-date",
"gix-hash",
"gix-hashtable",
"gix-object",
+ "gix-revwalk",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-revwalk"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc2623ba8747914f151f5e12b65adac576ab459dbed5f50a36c7a3e9cbf2d3ca"
+dependencies = [
+ "gix-commitgraph",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "smallvec",
"thiserror",
]
[[package]]
name = "gix-sec"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "794520043d5a024dfeac335c6e520cb616f6963e30dab995892382e998c12897"
+checksum = "b2b7b38b766eb95dcc5350a9c450030b69892c0902fa35f4a6d0809273bd9dae"
dependencies = [
- "bitflags 2.2.1",
+ "bitflags 2.3.2",
"gix-path",
"libc",
"windows",
@@ -1511,10 +1567,11 @@ dependencies = [
[[package]]
name = "gix-tempfile"
-version = "5.0.2"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2ceb30a610e3f5f2d5f9a5114689fde507ba9417705a8cf3429604275b2153c"
+checksum = "b3785cb010e9dc5c446dfbf02bc1119fc17d3a48a27c029efcb3a3c32953eb10"
dependencies = [
+ "gix-fs",
"libc",
"once_cell",
"parking_lot",
@@ -1525,9 +1582,9 @@ dependencies = [
[[package]]
name = "gix-transport"
-version = "0.31.0"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f01c2bf7b989c679695ef635fc7d9e80072e08101be4b53193c8e8b649900102"
+checksum = "64a39ffed9a9078ed700605e064b15d7c6ae50aa65e7faa36ca6919e8081df15"
dependencies = [
"base64",
"bstr",
@@ -1544,9 +1601,9 @@ dependencies = [
[[package]]
name = "gix-traverse"
-version = "0.25.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5be1e807f288c33bb005075111886cceb43ed8a167b3182a0f62c186e2a0dd1"
+checksum = "b0842e984cb4bf26339dc559f3a1b8bf8cdb83547799b2b096822a59f87f33d9"
dependencies = [
"gix-hash",
"gix-hashtable",
@@ -1556,9 +1613,9 @@ dependencies = [
[[package]]
name = "gix-url"
-version = "0.18.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc77f89054297cc81491e31f1bab4027e554b5ef742a44bd7035db9a0f78b76"
+checksum = "f1663df25ac42047a2547618d2a6979a26f478073f6306997429235d2cd4c863"
dependencies = [
"bstr",
"gix-features",
@@ -1570,18 +1627,18 @@ dependencies = [
[[package]]
name = "gix-utils"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c10b69beac219acb8df673187a1f07dde2d74092f974fb3f9eb385aeb667c909"
+checksum = "dbcfcb150c7ef553d76988467d223254045bdcad0dc6724890f32fbe96415da5"
dependencies = [
"fastrand",
]
[[package]]
name = "gix-validate"
-version = "0.7.4"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bd629d3680773e1785e585d76fd4295b740b559cad9141517300d99a0c8c049"
+checksum = "57ea5845b506c7728b9d89f4227cc369a5fc5a1d5b26c3add0f0d323413a3a60"
dependencies = [
"bstr",
"thiserror",
@@ -1589,9 +1646,9 @@ dependencies = [
[[package]]
name = "gix-worktree"
-version = "0.17.0"
+version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10bf56a1f5037d84293ea6cece61d9f27c4866b1e13c1c95f37cf56b7da7af25"
+checksum = "d388ad962e8854402734a7387af8790f6bdbc8d05349052dab16ca4a0def50f6"
dependencies = [
"bstr",
"filetime",
@@ -1620,7 +1677,7 @@ version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
dependencies = [
- "aho-corasick",
+ "aho-corasick 0.7.20",
"bstr",
"fnv",
"log",
@@ -1761,9 +1818,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "idna"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@@ -1841,9 +1898,9 @@ dependencies = [
[[package]]
name = "io-lifetimes"
-version = "1.0.10"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.1",
"libc",
@@ -1894,9 +1951,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.61"
+version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
@@ -1930,15 +1987,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.144"
+version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "libgit2-sys"
-version = "0.15.1+1.6.4"
+version = "0.15.2+1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb4577bde8cdfc7d6a2a4bcb7b049598597de33ffd337276e9c7db6cd4a2cee7"
+checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa"
dependencies = [
"cc",
"libc",
@@ -1950,9 +2007,9 @@ dependencies = [
[[package]]
name = "libm"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
+checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]]
name = "libnghttp2-sys"
@@ -1980,9 +2037,9 @@ dependencies = [
[[package]]
name = "libz-sys"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
+checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
dependencies = [
"cc",
"libc",
@@ -1992,15 +2049,15 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
-version = "0.3.4"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "lock_api"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
@@ -2008,12 +2065,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.17"
+version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "maybe-async"
@@ -2056,9 +2110,9 @@ dependencies = [
[[package]]
name = "memoffset"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
@@ -2071,9 +2125,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
-version = "0.6.2"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
@@ -2134,9 +2188,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.17.1"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
@@ -2156,9 +2210,9 @@ dependencies = [
[[package]]
name = "openssl"
-version = "0.10.50"
+version = "0.10.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1"
+checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
@@ -2177,7 +2231,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.14",
+ "syn 2.0.18",
]
[[package]]
@@ -2188,18 +2242,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
-version = "111.25.3+1.1.1t"
+version = "111.26.0+1.1.1u"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c"
+checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
-version = "0.9.85"
+version = "0.9.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0"
+checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
dependencies = [
"cc",
"libc",
@@ -2272,15 +2326,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.2.16",
+ "redox_syscall 0.3.5",
"smallvec",
- "windows-sys 0.45.0",
+ "windows-targets",
]
[[package]]
@@ -2305,9 +2359,9 @@ dependencies = [
[[package]]
name = "pasetors"
-version = "0.6.6"
+version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824bf633b85dc1dece2eb07161627ba5d90a951597cd5dbf8d85f4d82b7aea69"
+checksum = "ba765699a309908d55950919a3445e9491453e89b2587b1b2abe4143a48894c0"
dependencies = [
"ct-codecs",
"ed25519-compact",
@@ -2341,15 +2395,15 @@ dependencies = [
[[package]]
name = "percent-encoding"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pest"
-version = "2.5.7"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122"
+checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70"
dependencies = [
"thiserror",
"ucd-trie",
@@ -2357,9 +2411,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.5.7"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15"
+checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb"
dependencies = [
"pest",
"pest_generator",
@@ -2367,22 +2421,22 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.5.7"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e"
+checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.14",
+ "syn 2.0.18",
]
[[package]]
name = "pest_meta"
-version = "2.5.7"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e"
+checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
dependencies = [
"once_cell",
"pest",
@@ -2401,9 +2455,9 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.26"
+version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plotters"
@@ -2463,47 +2517,46 @@ dependencies = [
[[package]]
name = "primeorder"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf8d3875361e28f7753baefef104386e7aa47642c93023356d97fdef4003bfb5"
+checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3"
dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro2"
-version = "1.0.64"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
+checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
[[package]]
name = "prodash"
-version = "23.1.2"
+version = "25.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9516b775656bc3e8985e19cd4b8c0c0de045095074e453d2c0a513b5f978392d"
+checksum = "3236ce1618b6da4c7b618e0143c4d5b5dc190f75f81c49f248221382f7e9e9ae"
dependencies = [
"parking_lot",
]
[[package]]
name = "proptest"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70"
+checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65"
dependencies = [
"bit-set",
"bitflags 1.3.2",
"byteorder",
"lazy_static",
"num-traits",
- "quick-error 2.0.1",
"rand",
"rand_chacha",
"rand_xorshift",
- "regex-syntax",
+ "regex-syntax 0.6.29",
"rusty-fork",
"tempfile",
"unarray",
@@ -2511,9 +2564,9 @@ dependencies = [
[[package]]
name = "pulldown-cmark"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
+checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
dependencies = [
"bitflags 1.3.2",
"memchr",
@@ -2534,9 +2587,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
-version = "1.0.26"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
@@ -2631,13 +2684,13 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.7.3"
+version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
+checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [
- "aho-corasick",
+ "aho-corasick 1.0.2",
"memchr",
- "regex-syntax",
+ "regex-syntax 0.7.2",
]
[[package]]
@@ -2653,12 +2706,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
+[[package]]
name = "resolver-tests"
version = "0.0.0"
dependencies = [
"cargo",
"cargo-util",
- "is-terminal",
"lazy_static",
"proptest",
"varisat",
@@ -2694,9 +2752,9 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.37.15"
+version = "0.37.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece"
+checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
dependencies = [
"bitflags 1.3.2",
"errno",
@@ -2764,9 +2822,9 @@ dependencies = [
[[package]]
name = "security-framework"
-version = "2.8.2"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
+checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
@@ -2777,9 +2835,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.8.0"
+version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
+checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
dependencies = [
"core-foundation-sys",
"libc",
@@ -2803,9 +2861,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.160"
+version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
+checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
@@ -2832,13 +2890,13 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.160"
+version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
+checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.14",
+ "syn 2.0.18",
]
[[package]]
@@ -2852,9 +2910,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.95"
+version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa 1.0.6",
"ryu",
@@ -2863,9 +2921,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.1"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
dependencies = [
"serde",
]
@@ -2957,9 +3015,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "snapbox"
-version = "0.4.10"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9615402f9cff539301119bdf2c2f328739cf2b45c2116666618fb6ac399f75bb"
+checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835"
dependencies = [
"anstream",
"anstyle",
@@ -2975,9 +3033,9 @@ dependencies = [
[[package]]
name = "snapbox-macros"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8e40c667388ed1cb5060f545d0013bf0a23efdfa6c5c3e9ef592de391cd860f"
+checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31"
dependencies = [
"anstream",
]
@@ -2994,9 +3052,9 @@ dependencies = [
[[package]]
name = "spki"
-version = "0.7.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e"
+checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
dependencies = [
"base64ct",
"der",
@@ -3025,9 +3083,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
@@ -3042,9 +3100,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.14"
+version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
@@ -3075,15 +3133,16 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.5.0"
+version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
dependencies = [
+ "autocfg",
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
"rustix",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -3131,7 +3190,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.14",
+ "syn 2.0.18",
]
[[package]]
@@ -3146,9 +3205,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.20"
+version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
+checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
dependencies = [
"itoa 1.0.6",
"libc",
@@ -3160,15 +3219,15 @@ dependencies = [
[[package]]
name = "time-core"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]]
name = "time-macros"
-version = "0.2.8"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
+checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
dependencies = [
"time-core",
]
@@ -3200,9 +3259,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.7.3"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
+checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
dependencies = [
"serde",
"serde_spanned",
@@ -3212,18 +3271,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.6.1"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
-version = "0.19.8"
+version = "0.19.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
+checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
"indexmap",
"serde",
@@ -3273,9 +3332,9 @@ checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552"
[[package]]
name = "unicode-ident"
-version = "1.0.8"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "unicode-normalization"
@@ -3300,9 +3359,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
-version = "2.3.1"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
@@ -3462,9 +3521,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.84"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -3472,24 +3531,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.84"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.18",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.84"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3497,28 +3556,28 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.84"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.18",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.84"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "web-sys"
-version = "0.3.61"
+version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3561,7 +3620,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets",
]
[[package]]
@@ -3581,35 +3640,11 @@ dependencies = [
[[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]]
@@ -3713,9 +3748,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
-version = "0.4.1"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
+checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
@@ -3737,7 +3772,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"cargo",
- "clap 4.2.1",
+ "clap 4.3.3",
"env_logger 0.10.0",
"log",
]
diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml
index 05c95c727..7e383be69 100644
--- a/src/tools/cargo/Cargo.toml
+++ b/src/tools/cargo/Cargo.toml
@@ -19,22 +19,22 @@ cargo-credential = { version = "0.2.0", path = "credential/cargo-credential" }
cargo-platform = { path = "crates/cargo-platform", version = "0.1.3" }
cargo-test-macro = { path = "crates/cargo-test-macro" }
cargo-test-support = { path = "crates/cargo-test-support" }
-cargo-util = { version = "0.2.4", path = "crates/cargo-util" }
+cargo-util = { version = "0.2.5", path = "crates/cargo-util" }
cargo_metadata = "0.14.0"
clap = "4.2.0"
core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] }
crates-io = { version = "0.37.0", path = "crates/crates-io" }
criterion = { version = "0.3.5", features = ["html_reports"] }
curl = "0.4.44"
-curl-sys = "0.4.61"
+curl-sys = "0.4.63"
env_logger = "0.10.0"
filetime = "0.2.9"
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
fwdansi = "1.1.0"
git2 = "0.17.1"
git2-curl = "0.18.0"
-gix = { version = "0.44.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
-gix-features-for-configuration-only = { version = "0.29.0", package = "gix-features", features = [ "parallel" ] }
+gix = { version = "0.45.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
+gix-features-for-configuration-only = { version = "0.30.0", package = "gix-features", features = [ "parallel" ] }
glob = "0.3.0"
handlebars = { version = "3.2.1", features = ["dir_source"] }
hex = "0.4.2"
@@ -45,7 +45,6 @@ humantime = "2.0.0"
ignore = "0.4.7"
im-rc = "15.0.0"
indexmap = "1"
-is-terminal = "0.4.4"
itertools = "0.10.0"
jobserver = "0.1.26"
lazy_static = "1.3.0"
@@ -56,7 +55,7 @@ log = "0.4.17"
memchr = "2.1.3"
miow = "0.5.0"
opener = "0.5"
-openssl ="0.10.50"
+openssl ="0.10.55"
os_info = "3.5.0"
pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] }
pathdiff = "0.2"
@@ -80,6 +79,7 @@ sha2 = "0.10.6"
shell-escape = "0.1.4"
snapbox = { version = "0.4.0", features = ["diff", "path"] }
strip-ansi-escapes = "0.1.0"
+syn = { version = "2.0.14", features = ["extra-traits", "full"] }
tar = { version = "0.4.39", default-features = false }
tempfile = "3.1.0"
termcolor = "1.1.2"
@@ -95,7 +95,7 @@ windows-sys = "0.48"
[package]
name = "cargo"
-version = "0.72.1"
+version = "0.73.0"
edition = "2021"
license = "MIT OR Apache-2.0"
homepage = "https://crates.io"
@@ -136,10 +136,8 @@ humantime.workspace = true
ignore.workspace = true
im-rc.workspace = true
indexmap.workspace = true
-is-terminal.workspace = true
itertools.workspace = true
jobserver.workspace = true
-lazy_static.workspace = true
lazycell.workspace = true
libc.workspace = true
libgit2-sys.workspace = true
@@ -150,6 +148,7 @@ os_info.workspace = true
pasetors.workspace = true
pathdiff.workspace = true
pretty_env_logger = { workspace = true, optional = true }
+pulldown-cmark.workspace = true
rand.workspace = true
rustfix.workspace = true
semver.workspace = true
@@ -160,6 +159,7 @@ serde_json = { workspace = true, features = ["raw_value"] }
sha1.workspace = true
shell-escape.workspace = true
strip-ansi-escapes.workspace = true
+syn.workspace = true
tar.workspace = true
tempfile.workspace = true
termcolor.workspace = true
@@ -183,11 +183,8 @@ features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_Console",
- "Win32_System_IO",
"Win32_System_Threading",
"Win32_System_JobObjects",
- "Win32_Security",
- "Win32_System_SystemServices"
]
[dev-dependencies]
diff --git a/src/tools/cargo/README.md b/src/tools/cargo/README.md
index 423555e62..1d806b978 100644
--- a/src/tools/cargo/README.md
+++ b/src/tools/cargo/README.md
@@ -13,7 +13,7 @@ Cargo downloads your Rust project’s dependencies and compiles your project.
[![CI](https://github.com/rust-lang/cargo/actions/workflows/main.yml/badge.svg?branch=auto-cargo)](https://github.com/rust-lang/cargo/actions/workflows/main.yml)
-Code documentation: https://docs.rs/cargo/
+Code documentation: <https://doc.rust-lang.org/nightly/nightly-rustc/cargo/>
## Installing Cargo
diff --git a/src/tools/cargo/clippy.toml b/src/tools/cargo/clippy.toml
index 4f9be8f9b..050cc8716 100644
--- a/src/tools/cargo/clippy.toml
+++ b/src/tools/cargo/clippy.toml
@@ -1,3 +1,5 @@
+allow-print-in-tests = true
+allow-dbg-in-tests = true
disallowed-methods = [
{ path = "std::env::var", reason = "Use `Config::get_env` instead. See rust-lang/cargo#11588" },
{ path = "std::env::var_os", reason = "Use `Config::get_env_os` instead. See rust-lang/cargo#11588" },
diff --git a/src/tools/cargo/crates/cargo-test-support/src/containers.rs b/src/tools/cargo/crates/cargo-test-support/src/containers.rs
index 17040d82a..22fd5fd85 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/containers.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/containers.rs
@@ -94,7 +94,9 @@ impl Container {
let image_base = self.build_context.file_name().unwrap();
let image_name = format!("cargo-test-{}", image_base.to_str().unwrap());
- let _lock = BUILD_LOCK.lock().unwrap();
+ let _lock = BUILD_LOCK
+ .lock()
+ .map_err(|_| panic!("previous docker build failed, unable to run test"));
ProcessBuilder::new("docker")
.args(&["build", "--tag", image_name.as_str()])
.arg(&self.build_context)
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 d27aab44f..a2fa54c60 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
@@ -110,7 +110,9 @@ impl FileBuilder {
fn mk(&mut self) {
if self.executable {
- self.path.set_extension(env::consts::EXE_EXTENSION);
+ let mut path = self.path.clone().into_os_string();
+ write!(path, "{}", env::consts::EXE_SUFFIX).unwrap();
+ self.path = path.into();
}
self.dirname().mkdir_p();
@@ -1259,6 +1261,8 @@ pub trait TestEnv: Sized {
.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "stable")
// Keeps cargo within its sandbox.
.env("__CARGO_TEST_DISABLE_GLOBAL_KNOWN_HOST", "1")
+ // Set retry sleep to 1 millisecond.
+ .env("__CARGO_TEST_FIXED_RETRY_SLEEP_MS", "1")
// Incremental generates a huge amount of data per test, which we
// don't particularly need. Tests that specifically need to check
// the incremental behavior should turn this back on.
diff --git a/src/tools/cargo/crates/cargo-test-support/src/paths.rs b/src/tools/cargo/crates/cargo-test-support/src/paths.rs
index ef1fddb70..50040e1d4 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/paths.rs
@@ -1,7 +1,6 @@
use filetime::{self, FileTime};
-use lazy_static::lazy_static;
+
use std::cell::RefCell;
-use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{self, ErrorKind};
@@ -9,15 +8,11 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
+use std::sync::OnceLock;
static CARGO_INTEGRATION_TEST_DIR: &str = "cit";
-lazy_static! {
- // TODO: Use `SyncOnceCell` when stable
- static ref GLOBAL_ROOT: Mutex<Option<PathBuf>> = Mutex::new(None);
-
- static ref TEST_ROOTS: Mutex<HashMap<String, PathBuf>> = Default::default();
-}
+static GLOBAL_ROOT: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
/// This is used when running cargo is pre-CARGO_TARGET_TMPDIR
/// TODO: Remove when CARGO_TARGET_TMPDIR grows old enough.
@@ -31,7 +26,10 @@ fn global_root_legacy() -> PathBuf {
}
fn set_global_root(tmp_dir: Option<&'static str>) {
- let mut lock = GLOBAL_ROOT.lock().unwrap();
+ let mut lock = GLOBAL_ROOT
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
if lock.is_none() {
let mut root = match tmp_dir {
Some(tmp_dir) => PathBuf::from(tmp_dir),
@@ -44,7 +42,10 @@ fn set_global_root(tmp_dir: Option<&'static str>) {
}
pub fn global_root() -> PathBuf {
- let lock = GLOBAL_ROOT.lock().unwrap();
+ let lock = GLOBAL_ROOT
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
match lock.as_ref() {
Some(p) => p.clone(),
None => unreachable!("GLOBAL_ROOT not set yet"),
diff --git a/src/tools/cargo/crates/cargo-test-support/src/registry.rs b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
index 0cf82cb70..910f95bfa 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
@@ -1342,7 +1342,7 @@ impl Package {
/// Sets the index schema version for this package.
///
- /// See `cargo::sources::registry::RegistryPackage` for more information.
+ /// See `cargo::sources::registry::IndexPackage` for more information.
pub fn schema_version(&mut self, version: u32) -> &mut Package {
self.v = Some(version);
self
diff --git a/src/tools/cargo/crates/cargo-test-support/src/tools.rs b/src/tools/cargo/crates/cargo-test-support/src/tools.rs
index 7c056b6fa..2ce2849ae 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/tools.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/tools.rs
@@ -1,20 +1,21 @@
//! Common executables that can be reused by various tests.
use crate::{basic_manifest, paths, project, Project};
-use lazy_static::lazy_static;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
+use std::sync::OnceLock;
-lazy_static! {
- static ref ECHO_WRAPPER: Mutex<Option<PathBuf>> = Mutex::new(None);
- static ref ECHO: Mutex<Option<PathBuf>> = Mutex::new(None);
-}
+static ECHO_WRAPPER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
+static ECHO: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
/// Returns the path to an executable that works as a wrapper around rustc.
///
/// The wrapper will echo the command line it was called with to stderr.
pub fn echo_wrapper() -> PathBuf {
- let mut lock = ECHO_WRAPPER.lock().unwrap();
+ let mut lock = ECHO_WRAPPER
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
if let Some(path) = &*lock {
return path.clone();
}
@@ -53,7 +54,7 @@ pub fn echo_wrapper() -> PathBuf {
///
/// Do not expect this to be anything fancy.
pub fn echo() -> PathBuf {
- let mut lock = ECHO.lock().unwrap();
+ let mut lock = ECHO.get_or_init(|| Default::default()).lock().unwrap();
if let Some(path) = &*lock {
return path.clone();
}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index f01705fca..614581037 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.4"
+version = "0.2.5"
edition = "2021"
license = "MIT OR Apache-2.0"
homepage = "https://github.com/rust-lang/cargo"
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index 69df7a209..4a917821b 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -55,6 +55,8 @@ pub fn dylib_path_envvar() -> &'static str {
// penalty starting in 10.13. Cargo's testsuite ran more than twice as
// slow with it on CI.
"DYLD_FALLBACK_LIBRARY_PATH"
+ } else if cfg!(target_os = "aix") {
+ "LIBPATH"
} else {
"LD_LIBRARY_PATH"
}
@@ -411,11 +413,22 @@ fn _create_dir_all(p: &Path) -> Result<()> {
Ok(())
}
-/// Recursively remove all files and directories at the given directory.
+/// Equivalent to [`std::fs::remove_dir_all`] with better error messages.
///
/// This does *not* follow symlinks.
pub fn remove_dir_all<P: AsRef<Path>>(p: P) -> Result<()> {
- _remove_dir_all(p.as_ref())
+ _remove_dir_all(p.as_ref()).or_else(|prev_err| {
+ // `std::fs::remove_dir_all` is highly specialized for different platforms
+ // and may be more reliable than a simple walk. We try the walk first in
+ // order to report more detailed errors.
+ fs::remove_dir_all(p.as_ref()).with_context(|| {
+ format!(
+ "{:?}\n\nError: failed to remove directory `{}`",
+ prev_err,
+ p.as_ref().display(),
+ )
+ })
+ })
}
fn _remove_dir_all(p: &Path) -> Result<()> {
diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml
index 8a7cab113..e0efb9b6d 100644
--- a/src/tools/cargo/crates/resolver-tests/Cargo.toml
+++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml
@@ -7,7 +7,6 @@ publish = false
[dependencies]
cargo.workspace = true
cargo-util.workspace = true
-is-terminal.workspace = true
lazy_static.workspace = true
proptest.workspace = true
varisat.workspace = true
diff --git a/src/tools/cargo/crates/resolver-tests/src/lib.rs b/src/tools/cargo/crates/resolver-tests/src/lib.rs
index 01d9b5e6d..ab34e8663 100644
--- a/src/tools/cargo/crates/resolver-tests/src/lib.rs
+++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs
@@ -179,7 +179,6 @@ pub fn resolve_with_config_raw(
used: HashSet::new(),
};
let summary = Summary::new(
- config,
pkg_id("root"),
deps,
&BTreeMap::new(),
@@ -581,7 +580,6 @@ pub fn pkg_dep<T: ToPkgId>(name: T, dep: Vec<Dependency>) -> Summary {
None
};
Summary::new(
- &Config::default().unwrap(),
name.to_pkgid(),
dep,
&BTreeMap::new(),
@@ -610,7 +608,6 @@ pub fn pkg_loc(name: &str, loc: &str) -> Summary {
None
};
Summary::new(
- &Config::default().unwrap(),
pkg_id_loc(name, loc),
Vec::new(),
&BTreeMap::new(),
@@ -625,7 +622,6 @@ pub fn remove_dep(sum: &Summary, ind: usize) -> Summary {
deps.remove(ind);
// note: more things will need to be copied over in the future, but it works for now.
Summary::new(
- &Config::default().unwrap(),
sum.package_id(),
deps,
&BTreeMap::new(),
diff --git a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
index df74826f0..02486bfb5 100644
--- a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
+++ b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
@@ -1,3 +1,5 @@
+use std::io::IsTerminal;
+
use cargo::core::dependency::DepKind;
use cargo::core::Dependency;
use cargo::util::Config;
@@ -21,7 +23,7 @@ use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig {
max_shrink_iters:
- if is_ci() || !is_terminal::IsTerminal::is_terminal(&std::io::stderr()){
+ if is_ci() || !std::io::stderr().is_terminal() {
// This attempts to make sure that CI will fail fast,
0
} else {
diff --git a/src/tools/cargo/credential/cargo-credential-1password/README.md b/src/tools/cargo/credential/cargo-credential-1password/README.md
new file mode 100644
index 000000000..7cc15e05b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-1password/README.md
@@ -0,0 +1,7 @@
+# cargo-credential-1password
+
+This is the implementation for the Cargo credential helper for [1password].
+See the [credential-process] documentation for how to use this.
+
+[1password]: https://1password.com/
+[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md b/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md
new file mode 100644
index 000000000..7a4b02838
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md
@@ -0,0 +1,7 @@
+# cargo-credential-gnome-secret
+
+This is the implementation for the Cargo credential helper for [GNOME libsecret].
+See the [credential-process] documentation for how to use this.
+
+[GNOME libsecret]: https://wiki.gnome.org/Projects/Libsecret
+[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs
index 9283535af..8bb86ee43 100644
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs
+++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs
@@ -1,3 +1,8 @@
fn main() {
- pkg_config::probe_library("libsecret-1").unwrap();
+ if cfg!(target_os = "linux") {
+ // TODO: Consider ignoring errors when libsecret is not installed and
+ // switching the impl to UnsupportedCredential (possibly along with a
+ // warning?).
+ pkg_config::probe_library("libsecret-1").unwrap();
+ }
}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs
new file mode 100644
index 000000000..c584eeecf
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs
@@ -0,0 +1,190 @@
+//! Implementation of the libsecret credential helper.
+
+use cargo_credential::{Credential, Error};
+use std::ffi::{CStr, CString};
+use std::os::raw::{c_char, c_int};
+use std::ptr::{null, null_mut};
+
+#[allow(non_camel_case_types)]
+type gchar = c_char;
+
+#[allow(non_camel_case_types)]
+type gboolean = c_int;
+
+type GQuark = u32;
+
+#[repr(C)]
+struct GError {
+ domain: GQuark,
+ code: c_int,
+ message: *mut gchar,
+}
+
+#[repr(C)]
+struct GCancellable {
+ _private: [u8; 0],
+}
+
+#[repr(C)]
+struct SecretSchema {
+ name: *const gchar,
+ flags: SecretSchemaFlags,
+ attributes: [SecretSchemaAttribute; 32],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct SecretSchemaAttribute {
+ name: *const gchar,
+ attr_type: SecretSchemaAttributeType,
+}
+
+#[repr(C)]
+enum SecretSchemaFlags {
+ None = 0,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+enum SecretSchemaAttributeType {
+ String = 0,
+}
+
+extern "C" {
+ fn secret_password_store_sync(
+ schema: *const SecretSchema,
+ collection: *const gchar,
+ label: *const gchar,
+ password: *const gchar,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> gboolean;
+ fn secret_password_clear_sync(
+ schema: *const SecretSchema,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> gboolean;
+ fn secret_password_lookup_sync(
+ schema: *const SecretSchema,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> *mut gchar;
+}
+
+pub struct GnomeSecret;
+
+fn label(index_url: &str) -> CString {
+ CString::new(format!("cargo-registry:{}", index_url)).unwrap()
+}
+
+fn schema() -> SecretSchema {
+ let mut attributes = [SecretSchemaAttribute {
+ name: null(),
+ attr_type: SecretSchemaAttributeType::String,
+ }; 32];
+ attributes[0] = SecretSchemaAttribute {
+ name: b"url\0".as_ptr() as *const gchar,
+ attr_type: SecretSchemaAttributeType::String,
+ };
+ SecretSchema {
+ name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
+ flags: SecretSchemaFlags::None,
+ attributes,
+ }
+}
+
+impl Credential for GnomeSecret {
+ fn name(&self) -> &'static str {
+ env!("CARGO_PKG_NAME")
+ }
+
+ fn get(&self, index_url: &str) -> Result<String, Error> {
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ let index_url_c = CString::new(index_url).unwrap();
+ let schema = schema();
+ unsafe {
+ let token_c = secret_password_lookup_sync(
+ &schema,
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to get token: {}",
+ CStr::from_ptr((*error).message).to_str()?
+ )
+ .into());
+ }
+ if token_c.is_null() {
+ return Err(format!("cannot find token for {}", index_url).into());
+ }
+ let token = CStr::from_ptr(token_c)
+ .to_str()
+ .map_err(|e| format!("expected utf8 token: {}", e))?
+ .to_string();
+ Ok(token)
+ }
+ }
+
+ fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
+ let label = label(name.unwrap_or(index_url));
+ let token = CString::new(token).unwrap();
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ let index_url_c = CString::new(index_url).unwrap();
+ let schema = schema();
+ unsafe {
+ secret_password_store_sync(
+ &schema,
+ b"default\0".as_ptr() as *const gchar,
+ label.as_ptr(),
+ token.as_ptr(),
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to store token: {}",
+ CStr::from_ptr((*error).message).to_str()?
+ )
+ .into());
+ }
+ }
+ Ok(())
+ }
+
+ fn erase(&self, index_url: &str) -> Result<(), Error> {
+ let schema = schema();
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ let index_url_c = CString::new(index_url).unwrap();
+ unsafe {
+ secret_password_clear_sync(
+ &schema,
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to erase token: {}",
+ CStr::from_ptr((*error).message).to_str()?
+ )
+ .into());
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs
index 40972b05d..1d2ecc61f 100644
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs
+++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs
@@ -1,193 +1,11 @@
//! Cargo registry gnome libsecret credential process.
-use cargo_credential::{Credential, Error};
-use std::ffi::{CStr, CString};
-use std::os::raw::{c_char, c_int};
-use std::ptr::{null, null_mut};
-
-#[allow(non_camel_case_types)]
-type gchar = c_char;
-
-#[allow(non_camel_case_types)]
-type gboolean = c_int;
-
-type GQuark = u32;
-
-#[repr(C)]
-struct GError {
- domain: GQuark,
- code: c_int,
- message: *mut gchar,
-}
-
-#[repr(C)]
-struct GCancellable {
- _private: [u8; 0],
-}
-
-#[repr(C)]
-struct SecretSchema {
- name: *const gchar,
- flags: SecretSchemaFlags,
- attributes: [SecretSchemaAttribute; 32],
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-struct SecretSchemaAttribute {
- name: *const gchar,
- attr_type: SecretSchemaAttributeType,
-}
-
-#[repr(C)]
-enum SecretSchemaFlags {
- None = 0,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-enum SecretSchemaAttributeType {
- String = 0,
-}
-
-extern "C" {
- fn secret_password_store_sync(
- schema: *const SecretSchema,
- collection: *const gchar,
- label: *const gchar,
- password: *const gchar,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> gboolean;
- fn secret_password_clear_sync(
- schema: *const SecretSchema,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> gboolean;
- fn secret_password_lookup_sync(
- schema: *const SecretSchema,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> *mut gchar;
-}
-
-struct GnomeSecret;
-
-fn label(index_url: &str) -> CString {
- CString::new(format!("cargo-registry:{}", index_url)).unwrap()
-}
-
-fn schema() -> SecretSchema {
- let mut attributes = [SecretSchemaAttribute {
- name: null(),
- attr_type: SecretSchemaAttributeType::String,
- }; 32];
- attributes[0] = SecretSchemaAttribute {
- name: b"url\0".as_ptr() as *const gchar,
- attr_type: SecretSchemaAttributeType::String,
- };
- SecretSchema {
- name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
- flags: SecretSchemaFlags::None,
- attributes,
- }
-}
-
-impl Credential for GnomeSecret {
- fn name(&self) -> &'static str {
- env!("CARGO_PKG_NAME")
- }
-
- fn get(&self, index_url: &str) -> Result<String, Error> {
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- let schema = schema();
- unsafe {
- let token_c = secret_password_lookup_sync(
- &schema,
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to get token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- if token_c.is_null() {
- return Err(format!("cannot find token for {}", index_url).into());
- }
- let token = CStr::from_ptr(token_c)
- .to_str()
- .map_err(|e| format!("expected utf8 token: {}", e))?
- .to_string();
- Ok(token)
- }
- }
-
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
- let label = label(name.unwrap_or(index_url));
- let token = CString::new(token).unwrap();
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- let schema = schema();
- unsafe {
- secret_password_store_sync(
- &schema,
- b"default\0".as_ptr() as *const gchar,
- label.as_ptr(),
- token.as_ptr(),
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to store token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- }
- Ok(())
- }
-
- fn erase(&self, index_url: &str) -> Result<(), Error> {
- let schema = schema();
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- unsafe {
- secret_password_clear_sync(
- &schema,
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to erase token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- }
- Ok(())
- }
-}
+#[cfg(target_os = "linux")]
+mod libsecret;
+#[cfg(not(target_os = "linux"))]
+use cargo_credential::UnsupportedCredential as GnomeSecret;
+#[cfg(target_os = "linux")]
+use libsecret::GnomeSecret;
fn main() {
cargo_credential::main(GnomeSecret);
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md
new file mode 100644
index 000000000..554116b55
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md
@@ -0,0 +1,7 @@
+# cargo-credential-macos-keychain
+
+This is the implementation for the Cargo credential helper for [macOS Keychain].
+See the [credential-process] documentation for how to use this.
+
+[macOS Keychain]: https://support.apple.com/guide/keychain-access/welcome/mac
+[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/README.md b/src/tools/cargo/credential/cargo-credential-wincred/README.md
new file mode 100644
index 000000000..8c8d18789
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/README.md
@@ -0,0 +1,7 @@
+# cargo-credential-wincred
+
+This is the implementation for the Cargo credential helper for [Windows Credential Manager].
+See the [credential-process] documentation for how to use this.
+
+[Windows Credential Manager]: https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0
+[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process
diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs
index 946816571..db52bc8f2 100644
--- a/src/tools/cargo/src/bin/cargo/cli.rs
+++ b/src/tools/cargo/src/bin/cargo/cli.rs
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Context as _};
use cargo::core::shell::Shell;
use cargo::core::{features, CliUnstable};
-use cargo::{self, drop_print, drop_println, CliResult, Config};
+use cargo::{self, drop_print, drop_println, CargoResult, CliResult, Config};
use clap::{Arg, ArgMatches};
use itertools::Itertools;
use std::collections::HashMap;
@@ -14,16 +14,6 @@ use super::list_commands;
use crate::command_prelude::*;
use cargo::core::features::HIDDEN;
-lazy_static::lazy_static! {
- // Maps from commonly known external commands (not builtin to cargo) to their
- // description, for the help page. Reserved for external subcommands that are
- // core within the rust ecosystem (esp ones that might become internal in the future).
- static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS: HashMap<&'static str, &'static str> = HashMap::from([
- ("clippy", "Checks a package to catch common mistakes and improve your Rust code."),
- ("fmt", "Formats all bin and lib files of the current crate using rustfmt."),
- ]);
-}
-
pub fn main(config: &mut LazyConfig) -> CliResult {
let args = cli().try_get_matches()?;
@@ -128,15 +118,28 @@ Run with 'cargo -Z [FLAG] [COMMAND]'",
}
if expanded_args.flag("list") {
+ // Maps from commonly known external commands (not builtin to cargo)
+ // to their description, for the help page. Reserved for external
+ // subcommands that are core within the rust ecosystem (esp ones that
+ // might become internal in the future).
+ let known_external_command_descriptions = HashMap::from([
+ (
+ "clippy",
+ "Checks a package to catch common mistakes and improve your Rust code.",
+ ),
+ (
+ "fmt",
+ "Formats all bin and lib files of the current crate using rustfmt.",
+ ),
+ ]);
drop_println!(config, "Installed Commands:");
for (name, command) in list_commands(config) {
- let known_external_desc = KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS.get(name.as_str());
+ let known_external_desc = known_external_command_descriptions.get(name.as_str());
match command {
CommandInfo::BuiltIn { about } => {
assert!(
known_external_desc.is_none(),
- "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"",
- name
+ "known_external_commands shouldn't contain builtin `{name}`",
);
let summary = about.unwrap_or_default();
let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
@@ -172,10 +175,11 @@ Run with 'cargo -Z [FLAG] [COMMAND]'",
return Ok(());
}
};
- config_configure(config, &expanded_args, subcommand_args, global_args)?;
+ let exec = Exec::infer(cmd)?;
+ config_configure(config, &expanded_args, subcommand_args, global_args, &exec)?;
super::init_git(config);
- execute_subcommand(config, cmd, subcommand_args)
+ exec.exec(config, subcommand_args)
}
pub fn get_version_string(is_verbose: bool) -> String {
@@ -255,7 +259,7 @@ fn expand_aliases(
args: ArgMatches,
mut already_expanded: Vec<String>,
) -> Result<(ArgMatches, GlobalArgs), CliError> {
- if let Some((cmd, args)) = args.subcommand() {
+ if let Some((cmd, sub_args)) = args.subcommand() {
let exec = commands::builtin_exec(cmd);
let aliased_cmd = super::aliased_command(config, cmd);
@@ -271,7 +275,7 @@ fn expand_aliases(
// Here we ignore errors from aliasing as we already favor built-in command,
// and alias doesn't involve in this context.
- if let Some(values) = args.get_many::<OsString>("") {
+ if let Some(values) = sub_args.get_many::<OsString>("") {
// Command is built-in and is not conflicting with alias, but contains ignored values.
return Err(anyhow::format_err!(
"\
@@ -302,17 +306,34 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue
))?;
}
}
+ if commands::run::is_manifest_command(cmd) {
+ if config.cli_unstable().script {
+ return Ok((args, GlobalArgs::default()));
+ } else {
+ config.shell().warn(format_args!(
+ "\
+user-defined alias `{cmd}` has the appearance of a manfiest-command
+This was previously accepted but will be phased out when `-Zscript` is stabilized.
+For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>."
+ ))?;
+ }
+ }
let mut alias = alias
.into_iter()
.map(|s| OsString::from(s))
.collect::<Vec<_>>();
- alias.extend(args.get_many::<OsString>("").unwrap_or_default().cloned());
+ alias.extend(
+ sub_args
+ .get_many::<OsString>("")
+ .unwrap_or_default()
+ .cloned(),
+ );
// new_args strips out everything before the subcommand, so
// capture those global options now.
// Note that an alias to an external command will not receive
// these arguments. That may be confusing, but such is life.
- let global_args = GlobalArgs::new(args);
+ let global_args = GlobalArgs::new(sub_args);
let new_args = cli().no_binary_name(true).try_get_matches_from(alias)?;
let new_cmd = new_args.subcommand_name().expect("subcommand is required");
@@ -343,12 +364,26 @@ fn config_configure(
args: &ArgMatches,
subcommand_args: &ArgMatches,
global_args: GlobalArgs,
+ exec: &Exec,
) -> CliResult {
let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
- let verbose = global_args.verbose + args.verbose();
+ let mut verbose = global_args.verbose + args.verbose();
// quiet is unusual because it is redefined in some subcommands in order
// to provide custom help text.
- let quiet = args.flag("quiet") || subcommand_args.flag("quiet") || global_args.quiet;
+ let mut quiet = args.flag("quiet") || subcommand_args.flag("quiet") || global_args.quiet;
+ if matches!(exec, Exec::Manifest(_)) && !quiet {
+ // Verbosity is shifted quieter for `Exec::Manifest` as it is can be used as if you ran
+ // `cargo install` and we especially shouldn't pollute programmatic output.
+ //
+ // For now, interactive output has the same default output as `cargo run` but that is
+ // subject to change.
+ if let Some(lower) = verbose.checked_sub(1) {
+ verbose = lower;
+ } else if !config.shell().is_err_tty() {
+ // Don't pollute potentially-scripted output
+ quiet = true;
+ }
+ }
let global_color = global_args.color; // Extract so it can take reference.
let color = args
.get_one::<String>("color")
@@ -379,19 +414,65 @@ fn config_configure(
Ok(())
}
-fn execute_subcommand(config: &mut Config, cmd: &str, subcommand_args: &ArgMatches) -> CliResult {
- if let Some(exec) = commands::builtin_exec(cmd) {
- return exec(config, subcommand_args);
+enum Exec {
+ Builtin(commands::Exec),
+ Manifest(String),
+ External(String),
+}
+
+impl Exec {
+ /// Precedence isn't the most obvious from this function because
+ /// - Some is determined by `expand_aliases`
+ /// - Some is enforced by `avoid_ambiguity_between_builtins_and_manifest_commands`
+ ///
+ /// In actuality, it is:
+ /// 1. built-ins xor manifest-command
+ /// 2. aliases
+ /// 3. external subcommands
+ fn infer(cmd: &str) -> CargoResult<Self> {
+ if let Some(exec) = commands::builtin_exec(cmd) {
+ Ok(Self::Builtin(exec))
+ } else if commands::run::is_manifest_command(cmd) {
+ Ok(Self::Manifest(cmd.to_owned()))
+ } else {
+ Ok(Self::External(cmd.to_owned()))
+ }
}
- let mut ext_args: Vec<&OsStr> = vec![OsStr::new(cmd)];
- ext_args.extend(
- subcommand_args
- .get_many::<OsString>("")
- .unwrap_or_default()
- .map(OsString::as_os_str),
- );
- super::execute_external_subcommand(config, cmd, &ext_args)
+ fn exec(self, config: &mut Config, subcommand_args: &ArgMatches) -> CliResult {
+ match self {
+ Self::Builtin(exec) => exec(config, subcommand_args),
+ Self::Manifest(cmd) => {
+ let ext_path = super::find_external_subcommand(config, &cmd);
+ if !config.cli_unstable().script && ext_path.is_some() {
+ config.shell().warn(format_args!(
+ "\
+external subcommand `{cmd}` has the appearance of a manfiest-command
+This was previously accepted but will be phased out when `-Zscript` is stabilized.
+For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>.",
+ ))?;
+ Self::External(cmd).exec(config, subcommand_args)
+ } else {
+ let ext_args: Vec<OsString> = subcommand_args
+ .get_many::<OsString>("")
+ .unwrap_or_default()
+ .cloned()
+ .collect();
+ commands::run::exec_manifest_command(config, &cmd, &ext_args)
+ }
+ }
+ Self::External(cmd) => {
+ let mut ext_args = vec![OsStr::new(&cmd)];
+ ext_args.extend(
+ subcommand_args
+ .get_many::<OsString>("")
+ .unwrap_or_default()
+ .map(OsString::as_os_str),
+ );
+ super::execute_external_subcommand(config, &cmd, &ext_args)
+ }
+ }
+ }
}
#[derive(Default)]
@@ -435,9 +516,9 @@ pub fn cli() -> Command {
#[allow(clippy::disallowed_methods)]
let is_rustup = std::env::var_os("RUSTUP_HOME").is_some();
let usage = if is_rustup {
- "cargo [+toolchain] [OPTIONS] [COMMAND]"
+ "cargo [+toolchain] [OPTIONS] [COMMAND]\n cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..."
} else {
- "cargo [OPTIONS] [COMMAND]"
+ "cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <MANIFEST> [ARGS]..."
};
Command::new("cargo")
// Subcommands all count their args' display order independently (from 0),
@@ -567,3 +648,14 @@ impl LazyConfig {
fn verify_cli() {
cli().debug_assert();
}
+
+#[test]
+fn avoid_ambiguity_between_builtins_and_manifest_commands() {
+ for cmd in commands::builtin() {
+ let name = cmd.get_name();
+ assert!(
+ !commands::run::is_manifest_command(&name),
+ "built-in command {name} is ambiguous with manifest-commands"
+ )
+ }
+}
diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs
index 90c4f4dd5..52fc38b74 100644
--- a/src/tools/cargo/src/bin/cargo/commands/add.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/add.rs
@@ -242,7 +242,20 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult<Vec<
.flatten()
.map(|c| (Some(c.clone()), None))
.collect::<IndexMap<_, _>>();
+
let mut infer_crate_name = false;
+
+ for (crate_name, _) in crates.iter() {
+ let crate_name = crate_name.as_ref().unwrap();
+
+ if let Some(toolchain) = crate_name.strip_prefix("+") {
+ anyhow::bail!(
+ "invalid character `+` in dependency name: `+{toolchain}`
+ Use `cargo +{toolchain} add` if you meant to use the `{toolchain}` toolchain."
+ );
+ }
+ }
+
if crates.is_empty() {
if path.is_some() || git.is_some() {
crates.insert(None, None);
diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs
index 8197a1690..3bb90c2d5 100644
--- a/src/tools/cargo/src/bin/cargo/commands/install.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/install.rs
@@ -1,5 +1,6 @@
use crate::command_prelude::*;
+use anyhow::anyhow;
use cargo::core::{GitReference, SourceId, Workspace};
use cargo::ops;
use cargo::util::IntoUrl;
@@ -108,6 +109,16 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
.map(|k| resolve_crate(k, version))
.collect::<crate::CargoResult<Vec<_>>>()?;
+ for (crate_name, _) in krates.iter() {
+ if let Some(toolchain) = crate_name.strip_prefix("+") {
+ return Err(anyhow!(
+ "invalid character `+` in package name: `+{toolchain}`
+ Use `cargo +{toolchain} install` if you meant to use the `{toolchain}` toolchain."
+ )
+ .into());
+ }
+ }
+
let mut from_cwd = false;
let source = if let Some(url) = args.get_one::<String>("git") {
diff --git a/src/tools/cargo/src/bin/cargo/commands/mod.rs b/src/tools/cargo/src/bin/cargo/commands/mod.rs
index da3109260..b9da0e5fb 100644
--- a/src/tools/cargo/src/bin/cargo/commands/mod.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/mod.rs
@@ -43,7 +43,9 @@ pub fn builtin() -> Vec<Command> {
]
}
-pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResult> {
+pub type Exec = fn(&mut Config, &ArgMatches) -> CliResult;
+
+pub fn builtin_exec(cmd: &str) -> Option<Exec> {
let f = match cmd {
"add" => add::exec,
"bench" => bench::exec,
diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs
index cde754c7a..366e19396 100644
--- a/src/tools/cargo/src/bin/cargo/commands/run.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/run.rs
@@ -1,6 +1,11 @@
+use std::ffi::OsStr;
+use std::ffi::OsString;
+use std::path::Path;
+
use crate::command_prelude::*;
use crate::util::restricted_names::is_glob_pattern;
use cargo::core::Verbosity;
+use cargo::core::Workspace;
use cargo::ops::{self, CompileFilter, Packages};
use cargo_util::ProcessError;
@@ -13,7 +18,7 @@ pub fn cli() -> Command {
.arg(
Arg::new("args")
.help("Arguments for the binary or example to run")
- .value_parser(value_parser!(std::ffi::OsString))
+ .value_parser(value_parser!(OsString))
.num_args(0..)
.trailing_var_arg(true),
)
@@ -77,27 +82,64 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
}
};
- ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| {
- let proc_err = match err.downcast_ref::<ProcessError>() {
- Some(e) => e,
- None => return CliError::new(err, 101),
- };
-
- // If we never actually spawned the process then that sounds pretty
- // bad and we always want to forward that up.
- let exit_code = match proc_err.code {
- Some(exit) => exit,
- None => return CliError::new(err, 101),
- };
-
- // If `-q` was passed then we suppress extra error information about
- // a failed process, we assume the process itself printed out enough
- // information about why it failed so we don't do so as well
- let is_quiet = config.shell().verbosity() == Verbosity::Quiet;
- if is_quiet {
- CliError::code(exit_code)
- } else {
- CliError::new(err, exit_code)
- }
- })
+ ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| to_run_error(config, err))
+}
+
+/// See also `util/toml/mod.rs`s `is_embedded`
+pub fn is_manifest_command(arg: &str) -> bool {
+ let path = Path::new(arg);
+ 1 < path.components().count()
+ || path.extension() == Some(OsStr::new("rs"))
+ || path.file_name() == Some(OsStr::new("Cargo.toml"))
+}
+
+pub fn exec_manifest_command(config: &mut Config, cmd: &str, args: &[OsString]) -> CliResult {
+ if !config.cli_unstable().script {
+ return Err(anyhow::anyhow!("running `{cmd}` requires `-Zscript`").into());
+ }
+
+ let manifest_path = Path::new(cmd);
+ let manifest_path = root_manifest(Some(manifest_path), config)?;
+
+ // Treat `cargo foo.rs` like `cargo install --path foo` and re-evaluate the config based on the
+ // location where the script resides, rather than the environment from where it's being run.
+ let parent_path = manifest_path
+ .parent()
+ .expect("a file should always have a parent");
+ config.reload_rooted_at(parent_path)?;
+
+ let mut ws = Workspace::new(&manifest_path, config)?;
+ if config.cli_unstable().avoid_dev_deps {
+ ws.set_require_optional_deps(false);
+ }
+
+ let mut compile_opts =
+ cargo::ops::CompileOptions::new(config, cargo::core::compiler::CompileMode::Build)?;
+ compile_opts.spec = cargo::ops::Packages::Default;
+
+ cargo::ops::run(&ws, &compile_opts, args).map_err(|err| to_run_error(config, err))
+}
+
+fn to_run_error(config: &cargo::util::Config, err: anyhow::Error) -> CliError {
+ let proc_err = match err.downcast_ref::<ProcessError>() {
+ Some(e) => e,
+ None => return CliError::new(err, 101),
+ };
+
+ // If we never actually spawned the process then that sounds pretty
+ // bad and we always want to forward that up.
+ let exit_code = match proc_err.code {
+ Some(exit) => exit,
+ None => return CliError::new(err, 101),
+ };
+
+ // If `-q` was passed then we suppress extra error information about
+ // a failed process, we assume the process itself printed out enough
+ // information about why it failed so we don't do so as well
+ let is_quiet = config.shell().verbosity() == Verbosity::Quiet;
+ if is_quiet {
+ CliError::code(exit_code)
+ } else {
+ CliError::new(err, exit_code)
+ }
}
diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs
index 9fb6635ea..462332fb7 100644
--- a/src/tools/cargo/src/bin/cargo/main.rs
+++ b/src/tools/cargo/src/bin/cargo/main.rs
@@ -2,6 +2,8 @@
#![allow(clippy::all)]
#![warn(clippy::disallowed_methods)]
+use cargo::util::network::http::http_handle;
+use cargo::util::network::http::needs_custom_http_transport;
use cargo::util::toml::StringOrVec;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
@@ -293,12 +295,12 @@ fn init_git(config: &Config) {
/// configured to use libcurl instead of the built-in networking support so
/// that those configuration settings can be used.
fn init_git_transports(config: &Config) {
- match cargo::ops::needs_custom_http_transport(config) {
+ match needs_custom_http_transport(config) {
Ok(true) => {}
_ => return,
}
- let handle = match cargo::ops::http_handle(config) {
+ let handle = match http_handle(config) {
Ok(handle) => handle,
Err(..) => return,
};
diff --git a/src/tools/cargo/src/cargo/core/compiler/build_config.rs b/src/tools/cargo/src/cargo/core/compiler/build_config.rs
index 885b124b9..5d4d754bf 100644
--- a/src/tools/cargo/src/cargo/core/compiler/build_config.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/build_config.rs
@@ -1,4 +1,5 @@
use crate::core::compiler::CompileKind;
+use crate::util::config::JobsConfig;
use crate::util::interning::InternedString;
use crate::util::{CargoResult, Config, RustfixDiagnosticServer};
use anyhow::{bail, Context as _};
@@ -64,7 +65,7 @@ impl BuildConfig {
/// * `target.$target.libfoo.metadata`
pub fn new(
config: &Config,
- jobs: Option<i32>,
+ jobs: Option<JobsConfig>,
keep_going: bool,
requested_targets: &[String],
mode: CompileMode,
@@ -78,11 +79,22 @@ impl BuildConfig {
its environment, ignoring the `-j` parameter",
)?;
}
- let jobs = match jobs.or(cfg.jobs) {
+ let jobs = match jobs.or(cfg.jobs.clone()) {
None => default_parallelism()?,
- Some(0) => anyhow::bail!("jobs may not be 0"),
- Some(j) if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32,
- Some(j) => j as u32,
+ Some(value) => match value {
+ JobsConfig::Integer(j) => match j {
+ 0 => anyhow::bail!("jobs may not be 0"),
+ j if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32,
+ j => j as u32,
+ },
+ JobsConfig::String(j) => match j.as_str() {
+ "default" => default_parallelism()?,
+ _ => {
+ anyhow::bail!(
+ format!("could not parse `{j}`. Number of parallel jobs should be `default` or a number."))
+ }
+ },
+ },
};
if config.cli_unstable().build_std.is_some() && requested_kinds[0].is_host() {
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 01890e542..d17462174 100644
--- a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
@@ -31,7 +31,7 @@
//! [`CompileMode::RunCustomBuild`]: super::CompileMode
//! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
-use super::{fingerprint, Context, Job, LinkType, Unit, Work};
+use super::{fingerprint, Context, Job, Unit, Work};
use crate::core::compiler::artifact;
use crate::core::compiler::context::Metadata;
use crate::core::compiler::job_queue::JobState;
@@ -62,7 +62,7 @@ pub struct BuildOutput {
/// Names and link kinds of libraries, suitable for the `-l` flag.
pub library_links: Vec<String>,
/// Linker arguments suitable to be passed to `-C link-arg=<args>`
- pub linker_args: Vec<(LinkType, String)>,
+ pub linker_args: Vec<(LinkArgTarget, String)>,
/// Various `--cfg` flags to pass to the compiler.
pub cfgs: Vec<String>,
/// Various `--check-cfg` flags to pass to the compiler.
@@ -146,6 +146,47 @@ pub struct BuildDeps {
pub rerun_if_env_changed: Vec<String>,
}
+/// Represents one of the instructions from `cargo:rustc-link-arg-*` build
+/// script instruction family.
+///
+/// In other words, indicates targets that custom linker arguments applies to.
+///
+/// See the [build script documentation][1] for more.
+///
+/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-link-argflag
+#[derive(Clone, Hash, Debug, PartialEq, Eq)]
+pub enum LinkArgTarget {
+ /// Represents `cargo:rustc-link-arg=FLAG`.
+ All,
+ /// Represents `cargo:rustc-cdylib-link-arg=FLAG`.
+ Cdylib,
+ /// Represents `cargo:rustc-link-arg-bins=FLAG`.
+ Bin,
+ /// Represents `cargo:rustc-link-arg-bin=BIN=FLAG`.
+ SingleBin(String),
+ /// Represents `cargo:rustc-link-arg-tests=FLAG`.
+ Test,
+ /// Represents `cargo:rustc-link-arg-benches=FLAG`.
+ Bench,
+ /// Represents `cargo:rustc-link-arg-examples=FLAG`.
+ Example,
+}
+
+impl LinkArgTarget {
+ /// Checks if this link type applies to a given [`Target`].
+ pub fn applies_to(&self, target: &Target) -> bool {
+ match self {
+ LinkArgTarget::All => true,
+ LinkArgTarget::Cdylib => target.is_cdylib(),
+ LinkArgTarget::Bin => target.is_bin(),
+ LinkArgTarget::SingleBin(name) => target.is_bin() && target.name() == name,
+ LinkArgTarget::Test => target.is_test(),
+ LinkArgTarget::Bench => target.is_bench(),
+ LinkArgTarget::Example => target.is_exe_example(),
+ }
+ }
+}
+
/// Prepares a `Work` that executes the target as a custom build script.
pub fn prepare(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
let _p = profile::start(format!(
@@ -711,10 +752,10 @@ impl BuildOutput {
key, pkg_descr
));
}
- linker_args.push((LinkType::Cdylib, value))
+ linker_args.push((LinkArgTarget::Cdylib, value))
}
"rustc-link-arg-bins" => {
- check_and_add_target!("bin", Target::is_bin, LinkType::Bin);
+ check_and_add_target!("bin", Target::is_bin, LinkArgTarget::Bin);
}
"rustc-link-arg-bin" => {
let mut parts = value.splitn(2, '=');
@@ -742,19 +783,19 @@ impl BuildOutput {
bin_name
);
}
- linker_args.push((LinkType::SingleBin(bin_name), arg.to_string()));
+ linker_args.push((LinkArgTarget::SingleBin(bin_name), arg.to_string()));
}
"rustc-link-arg-tests" => {
- check_and_add_target!("test", Target::is_test, LinkType::Test);
+ check_and_add_target!("test", Target::is_test, LinkArgTarget::Test);
}
"rustc-link-arg-benches" => {
- check_and_add_target!("benchmark", Target::is_bench, LinkType::Bench);
+ check_and_add_target!("benchmark", Target::is_bench, LinkArgTarget::Bench);
}
"rustc-link-arg-examples" => {
- check_and_add_target!("example", Target::is_example, LinkType::Example);
+ check_and_add_target!("example", Target::is_example, LinkArgTarget::Example);
}
"rustc-link-arg" => {
- linker_args.push((LinkType::All, value));
+ linker_args.push((LinkArgTarget::All, value));
}
"rustc-cfg" => cfgs.push(value.to_string()),
"rustc-check-cfg" => {
diff --git a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
index a3523110b..aa8be50f7 100644
--- a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
@@ -1,8 +1,11 @@
-//! # Fingerprints
+//! Tracks changes to determine if something needs to be recompiled.
//!
//! This module implements change-tracking so that Cargo can know whether or
//! not something needs to be recompiled. A Cargo [`Unit`] can be either "dirty"
//! (needs to be recompiled) or "fresh" (it does not need to be recompiled).
+//!
+//! ## Mechanisms affecting freshness
+//!
//! There are several mechanisms that influence a Unit's freshness:
//!
//! - The [`Fingerprint`] is a hash, saved to the filesystem in the
diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs
index 7e49f0079..31e63c226 100644
--- a/src/tools/cargo/src/cargo/core/compiler/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs
@@ -76,6 +76,7 @@ pub use self::compilation::{Compilation, Doctest, UnitOutput};
pub use self::compile_kind::{CompileKind, CompileTarget};
pub use self::context::{Context, Metadata};
pub use self::crate_type::CrateType;
+pub use self::custom_build::LinkArgTarget;
pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
pub(crate) use self::fingerprint::DirtyReason;
pub use self::job_queue::Freshness;
@@ -99,44 +100,6 @@ use rustfix::diagnostics::Applicability;
const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
-// TODO: Rename this to `ExtraLinkArgFor` or else, and move to compiler/custom_build.rs?
-/// Represents one of the instruction from `cargo:rustc-link-arg-*` build script
-/// instruction family.
-///
-/// In other words, indicates targets that custom linker arguments applies to.
-#[derive(Clone, Hash, Debug, PartialEq, Eq)]
-pub enum LinkType {
- /// Represents `cargo:rustc-link-arg=FLAG`.
- All,
- /// Represents `cargo:rustc-cdylib-link-arg=FLAG`.
- Cdylib,
- /// Represents `cargo:rustc-link-arg-bins=FLAG`.
- Bin,
- /// Represents `cargo:rustc-link-arg-bin=BIN=FLAG`.
- SingleBin(String),
- /// Represents `cargo:rustc-link-arg-tests=FLAG`.
- Test,
- /// Represents `cargo:rustc-link-arg-benches=FLAG`.
- Bench,
- /// Represents `cargo:rustc-link-arg-examples=FLAG`.
- Example,
-}
-
-impl LinkType {
- /// Checks if this link type applies to a given [`Target`].
- pub fn applies_to(&self, target: &Target) -> bool {
- match self {
- LinkType::All => true,
- LinkType::Cdylib => target.is_cdylib(),
- LinkType::Bin => target.is_bin(),
- LinkType::SingleBin(name) => target.is_bin() && target.name() == name,
- LinkType::Test => target.is_test(),
- LinkType::Bench => target.is_bench(),
- LinkType::Example => target.is_exe_example(),
- }
- }
-}
-
/// A glorified callback for executing calls to rustc. Rather than calling rustc
/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
/// the build calls.
@@ -286,14 +249,12 @@ fn make_failed_scrape_diagnostic(
/// Creates a unit of work invoking `rustc` for building the `unit`.
fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> CargoResult<Work> {
- let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?;
+ let mut rustc = prepare_rustc(cx, unit)?;
let build_plan = cx.bcx.build_config.build_plan;
let name = unit.pkg.name().to_string();
let buildkey = unit.buildkey();
- add_cap_lints(cx.bcx, unit, &mut rustc);
-
let outputs = cx.outputs(unit)?;
let root = cx.files().out_dir(unit);
@@ -319,10 +280,6 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
let rustc_dep_info_loc = root.join(dep_info_name);
let dep_info_loc = fingerprint::dep_info_loc(cx, unit);
- rustc.args(cx.bcx.rustflags_args(unit));
- if cx.bcx.config.cli_unstable().binary_dep_depinfo {
- rustc.arg("-Z").arg("binary-dep-depinfo");
- }
let mut output_options = OutputOptions::new(cx, unit);
let package_id = unit.pkg.package_id();
let target = Target::clone(&unit.target);
@@ -544,7 +501,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
// clause should have been kept in the `if` block above. For
// now, continue allowing it for cdylib only.
// See https://github.com/rust-lang/cargo/issues/9562
- if lt.applies_to(target) && (key.0 == current_id || *lt == LinkType::Cdylib) {
+ if lt.applies_to(target) && (key.0 == current_id || *lt == LinkArgTarget::Cdylib) {
rustc.arg("-C").arg(format!("link-arg={}", arg));
}
}
@@ -604,7 +561,7 @@ fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResu
}
if json_messages {
- let debuginfo = profile.debuginfo.to_option().map(|d| match d {
+ let debuginfo = match profile.debuginfo.into_inner() {
TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
@@ -614,10 +571,10 @@ fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResu
TomlDebugInfo::LineTablesOnly => {
machine_message::ArtifactDebuginfo::Named("line-tables-only")
}
- });
+ };
let art_profile = machine_message::ArtifactProfile {
opt_level: profile.opt_level.as_str(),
- debuginfo,
+ debuginfo: Some(debuginfo),
debug_assertions: profile.debug_assertions,
overflow_checks: profile.overflow_checks,
test: unit_mode.is_any_test(),
@@ -705,13 +662,13 @@ where
search_path
}
-// TODO: do we really need this as a separate function?
-// Maybe we should reorganize `rustc` fn to make it more traceable and readable.
-fn prepare_rustc(
- cx: &mut Context<'_, '_>,
- crate_types: &[CrateType],
- unit: &Unit,
-) -> CargoResult<ProcessBuilder> {
+/// Prepares flags and environments we can compute for a `rustc` invocation
+/// before the job queue starts compiling any unit.
+///
+/// This builds a static view of the invocation. Flags depending on the
+/// completion of other units will be added later in runtime, such as flags
+/// from build scripts.
+fn prepare_rustc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
let is_primary = cx.is_primary_package(unit);
let is_workspace = cx.bcx.ws.is_member(&unit.pkg);
@@ -729,13 +686,23 @@ fn prepare_rustc(
}
base.inherit_jobserver(&cx.jobserver);
- build_base_args(cx, &mut base, unit, crate_types)?;
+ build_base_args(cx, &mut base, unit)?;
build_deps_args(&mut base, cx, unit)?;
+ add_cap_lints(cx.bcx, unit, &mut base);
+ base.args(cx.bcx.rustflags_args(unit));
+ if cx.bcx.config.cli_unstable().binary_dep_depinfo {
+ base.arg("-Z").arg("binary-dep-depinfo");
+ }
Ok(base)
}
-/// Creates a unit of work invoking `rustdoc` for documenting the `unit`.
-fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
+/// Prepares flags and environments we can compute for a `rustdoc` invocation
+/// before the job queue starts compiling any unit.
+///
+/// This builds a static view of the invocation. Flags depending on the
+/// completion of other units will be added later in runtime, such as flags
+/// from build scripts.
+fn prepare_rustdoc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
let bcx = cx.bcx;
// script_metadata is not needed here, it is only for tests.
let mut rustdoc = cx.compilation.rustdoc_process(unit, None)?;
@@ -749,12 +716,6 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
rustdoc.arg("--target").arg(target.rustc_target());
}
let doc_dir = cx.files().out_dir(unit);
-
- // Create the documentation directory ahead of time as rustdoc currently has
- // a bug where concurrent invocations will race to create this directory if
- // it doesn't already exist.
- paths::create_dir_all(&doc_dir)?;
-
rustdoc.arg("-o").arg(&doc_dir);
rustdoc.args(&features_args(unit));
rustdoc.args(&check_cfg_args(cx, unit));
@@ -770,10 +731,6 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
let metadata = cx.metadata_for_doc_units[unit];
rustdoc.arg("-C").arg(format!("metadata={}", metadata));
- let scrape_output_path = |unit: &Unit| -> CargoResult<PathBuf> {
- cx.outputs(unit).map(|outputs| outputs[0].path.clone())
- };
-
if unit.mode.is_doc_scrape() {
debug_assert!(cx.bcx.scrape_units.contains(unit));
@@ -785,7 +742,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
rustdoc
.arg("--scrape-examples-output-path")
- .arg(scrape_output_path(unit)?);
+ .arg(scrape_output_path(cx, unit)?);
// Only scrape example for items from crates in the workspace, to reduce generated file size
for pkg in cx.bcx.ws.members() {
@@ -800,21 +757,9 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
}
}
- let should_include_scrape_units = unit.mode.is_doc()
- && cx.bcx.scrape_units.len() > 0
- && cx.bcx.ws.unit_needs_doc_scrape(unit);
- let scrape_outputs = if should_include_scrape_units {
+ if should_include_scrape_units(cx.bcx, unit) {
rustdoc.arg("-Zunstable-options");
- Some(
- cx.bcx
- .scrape_units
- .iter()
- .map(|unit| Ok((cx.files().metadata(unit), scrape_output_path(unit)?)))
- .collect::<CargoResult<HashMap<_, _>>>()?,
- )
- } else {
- None
- };
+ }
build_deps_args(&mut rustdoc, cx, unit)?;
rustdoc::add_root_urls(cx, unit, &mut rustdoc)?;
@@ -825,6 +770,20 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
append_crate_version_flag(unit, &mut rustdoc);
}
+ Ok(rustdoc)
+}
+
+/// Creates a unit of work invoking `rustdoc` for documenting the `unit`.
+fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
+ let mut rustdoc = prepare_rustdoc(cx, unit)?;
+
+ let crate_name = unit.target.crate_name();
+ let doc_dir = cx.files().out_dir(unit);
+ // Create the documentation directory ahead of time as rustdoc currently has
+ // a bug where concurrent invocations will race to create this directory if
+ // it doesn't already exist.
+ paths::create_dir_all(&doc_dir)?;
+
let target_desc = unit.target.description_named();
let name = unit.pkg.name().to_string();
let build_script_outputs = Arc::clone(&cx.build_script_outputs);
@@ -833,6 +792,17 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
let target = Target::clone(&unit.target);
let mut output_options = OutputOptions::new(cx, unit);
let script_metadata = cx.find_build_script_metadata(unit);
+ let scrape_outputs = if should_include_scrape_units(cx.bcx, unit) {
+ Some(
+ cx.bcx
+ .scrape_units
+ .iter()
+ .map(|unit| Ok((cx.files().metadata(unit), scrape_output_path(cx, unit)?)))
+ .collect::<CargoResult<HashMap<_, _>>>()?,
+ )
+ } else {
+ None
+ };
let failed_scrape_units = Arc::clone(&cx.failed_scrape_units);
let hide_diagnostics_for_scrape_unit = cx.bcx.unit_can_fail_for_docscraping(unit)
@@ -977,12 +947,7 @@ fn add_error_format_and_color(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) {
}
/// Adds essential rustc flags and environment variables to the command to execute.
-fn build_base_args(
- cx: &mut Context<'_, '_>,
- cmd: &mut ProcessBuilder,
- unit: &Unit,
- crate_types: &[CrateType],
-) -> CargoResult<()> {
+fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit) -> CargoResult<()> {
assert!(!unit.mode.is_run_custom_build());
let bcx = cx.bcx;
@@ -998,7 +963,7 @@ fn build_base_args(
ref panic,
incremental,
strip,
- rustflags,
+ rustflags: profile_rustflags,
..
} = unit.profile.clone();
let test = unit.mode.is_any_test();
@@ -1014,7 +979,7 @@ fn build_base_args(
let mut contains_dy_lib = false;
if !test {
- for crate_type in crate_types {
+ for crate_type in &unit.target.rustc_crate_types() {
cmd.arg("--crate-type").arg(crate_type.as_str());
contains_dy_lib |= crate_type == &CrateType::Dylib;
}
@@ -1047,22 +1012,6 @@ fn build_base_args(
cmd.args(&lto_args(cx, unit));
- // This is generally just an optimization on build time so if we don't pass
- // it then it's ok. The values for the flag (off, packed, unpacked) may be supported
- // or not depending on the platform, so availability is checked per-value.
- // For example, at the time of writing this code, on Windows the only stable valid
- // value for split-debuginfo is "packed", while on Linux "unpacked" is also stable.
- if let Some(split) = split_debuginfo {
- if cx
- .bcx
- .target_data
- .info(unit.kind)
- .supports_debuginfo_split(split)
- {
- cmd.arg("-C").arg(format!("split-debuginfo={}", split));
- }
- }
-
if let Some(backend) = codegen_backend {
cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
}
@@ -1071,14 +1020,30 @@ fn build_base_args(
cmd.arg("-C").arg(&format!("codegen-units={}", n));
}
- if let Some(debuginfo) = debuginfo.to_option() {
- cmd.arg("-C").arg(format!("debuginfo={}", debuginfo));
+ let debuginfo = debuginfo.into_inner();
+ // Shorten the number of arguments if possible.
+ if debuginfo != TomlDebugInfo::None {
+ cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
+ // This is generally just an optimization on build time so if we don't
+ // pass it then it's ok. The values for the flag (off, packed, unpacked)
+ // may be supported or not depending on the platform, so availability is
+ // checked per-value. For example, at the time of writing this code, on
+ // Windows the only stable valid value for split-debuginfo is "packed",
+ // while on Linux "unpacked" is also stable.
+ if let Some(split) = split_debuginfo {
+ if cx
+ .bcx
+ .target_data
+ .info(unit.kind)
+ .supports_debuginfo_split(split)
+ {
+ cmd.arg("-C").arg(format!("split-debuginfo={split}"));
+ }
+ }
}
cmd.args(unit.pkg.manifest().lint_rustflags());
- if !rustflags.is_empty() {
- cmd.args(&rustflags);
- }
+ cmd.args(&profile_rustflags);
if let Some(args) = cx.bcx.extra_args_for(unit) {
cmd.args(args);
}
@@ -1277,11 +1242,7 @@ fn lto_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> {
///
/// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path
/// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located
-fn build_deps_args(
- cmd: &mut ProcessBuilder,
- cx: &mut Context<'_, '_>,
- unit: &Unit,
-) -> CargoResult<()> {
+fn build_deps_args(cmd: &mut ProcessBuilder, cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<()> {
let bcx = cx.bcx;
cmd.arg("-L").arg(&{
let mut deps = OsString::from("dependency=");
@@ -1821,3 +1782,14 @@ fn apply_env_config(config: &crate::Config, cmd: &mut ProcessBuilder) -> CargoRe
}
Ok(())
}
+
+/// Checks if there are some scrape units waiting to be processed.
+fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
+ unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
+}
+
+/// Gets the file path of function call information output from `rustdoc`.
+fn scrape_output_path(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
+ assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
+ cx.outputs(unit).map(|outputs| outputs[0].path.clone())
+}
diff --git a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
index 3bf8b0c77..369fd8318 100644
--- a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
@@ -1,4 +1,4 @@
-//! # Constructs the dependency graph for compilation
+//! Constructs the dependency graph for compilation.
//!
//! Rust code is typically organized as a set of Cargo packages. The
//! dependencies between the packages themselves are stored in the
diff --git a/src/tools/cargo/src/cargo/core/features.rs b/src/tools/cargo/src/cargo/core/features.rs
index d56054a0a..9b99d5a15 100644
--- a/src/tools/cargo/src/cargo/core/features.rs
+++ b/src/tools/cargo/src/cargo/core/features.rs
@@ -687,6 +687,26 @@ macro_rules! unstable_cli_options {
fields
}
}
+
+ #[cfg(test)]
+ mod test {
+ #[test]
+ fn ensure_sorted() {
+ // This will be printed out if the fields are not sorted.
+ let location = std::panic::Location::caller();
+ println!(
+ "\nTo fix this test, sort the features inside the macro at {}:{}\n",
+ location.file(),
+ location.line()
+ );
+ let mut expected = vec![$(stringify!($element)),*];
+ expected[2..].sort();
+ snapbox::assert_eq(
+ format!("{:#?}", expected),
+ format!("{:#?}", vec![$(stringify!($element)),*])
+ );
+ }
+ }
}
}
@@ -696,7 +716,7 @@ unstable_cli_options!(
print_im_a_teapot: bool = (HIDDEN),
// All other unstable features.
- // Please keep this list lexiographically ordered.
+ // Please keep this list lexicographically ordered.
advanced_env: bool = (HIDDEN),
avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"),
binary_dep_depinfo: bool = ("Track changes to dependency artifacts"),
@@ -704,34 +724,34 @@ 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"),
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"),
credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
- #[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"),
- doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"),
+ direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"),
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
features: Option<Vec<String>> = (HIDDEN),
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
- jobserver_per_rustc: bool = (HIDDEN),
+ host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"),
+ lints: bool = ("Pass `[lints]` to the linting tools"),
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
- direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"),
+ msrv_policy: bool = ("Enable rust-version aware policy within cargo"),
mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"),
+ next_lockfile_bump: bool = (HIDDEN),
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
- host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"),
+ publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
registry_auth: bool = ("Authentication for alternative registries, and generate registry authentication tokens using asymmetric cryptography"),
- target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"),
+ rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"),
+ script: bool = ("Enable support for single-file, `.rs` packages"),
separate_nightlies: bool = (HIDDEN),
- publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
- unstable_options: bool = ("Allow the usage of unstable options"),
skip_rustdoc_fingerprint: bool = (HIDDEN),
- rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"),
- msrv_policy: bool = ("Enable rust-version aware policy within cargo"),
- lints: bool = ("Pass `[lints]` to the linting tools"),
+ target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
+ unstable_options: bool = ("Allow the usage of unstable options"),
);
const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
@@ -779,6 +799,9 @@ const STABILIZED_NAMED_PROFILES: &str = "The named-profiles feature is now alway
See https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#custom-profiles \
for more information";
+const STABILIZED_DOCTEST_IN_WORKSPACE: &str =
+ "The doctest-in-workspace feature is now always enabled.";
+
const STABILIZED_FUTURE_INCOMPAT_REPORT: &str =
"The future-incompat-report feature is now always enabled.";
@@ -1011,41 +1034,19 @@ impl CliUnstable {
}
match k {
- "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
+ // Permanently unstable features
+ // Sorted alphabetically:
"allow-features" => self.allow_features = Some(parse_features(v).into_iter().collect()),
- "unstable-options" => self.unstable_options = parse_empty(k, v)?,
- "no-index-update" => self.no_index_update = parse_empty(k, v)?,
- "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
- "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
- "direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?,
- "advanced-env" => self.advanced_env = parse_empty(k, v)?,
- "config-include" => self.config_include = parse_empty(k, v)?,
- "check-cfg" => {
- self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))?
- }
- "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
- // can also be set in .cargo/config or with and ENV
- "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
- "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES),
- "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
- "bindeps" => self.bindeps = parse_empty(k, v)?,
- "build-std" => {
- self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
- }
- "build-std-features" => self.build_std_features = Some(parse_features(v)),
- "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
- "doctest-in-workspace" => self.doctest_in_workspace = parse_empty(k, v)?,
- "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
- "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
- "gitoxide" => {
- self.gitoxide = v.map_or_else(
- || Ok(Some(GitoxideFeatures::all())),
- |v| parse_gitoxide(v.split(',')),
- )?
- }
- "host-config" => self.host_config = parse_empty(k, v)?,
- "target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
- "publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
+ "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
+
+ // Stabilized features
+ // Sorted by version, then alphabetically:
+ "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS),
+ "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?,
+ "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES),
+ "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE),
+ "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE),
+ "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS),
"features" => {
// `-Z features` has been stabilized since 1.51,
// but `-Z features=compare` is still allowed for convenience
@@ -1067,35 +1068,66 @@ impl CliUnstable {
}
self.features = Some(feats);
}
- "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
- "multitarget" => stabilized_warn(k, "1.64", STABILISED_MULTITARGET),
- "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
- "terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH),
- "sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY),
- "registry-auth" => self.registry_auth = parse_empty(k, v)?,
- "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES),
- "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES),
- "credential-process" => self.credential_process = parse_empty(k, v)?,
- "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?,
- "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?,
- "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS),
- "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?,
- "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES),
- "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE),
- "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE),
- "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS),
"package-features" => stabilized_warn(k, "1.51", STABILIZED_PACKAGE_FEATURES),
- "extra-link-arg" => stabilized_warn(k, "1.56", STABILIZED_EXTRA_LINK_ARG),
"configurable-env" => stabilized_warn(k, "1.56", STABILIZED_CONFIGURABLE_ENV),
+ "extra-link-arg" => stabilized_warn(k, "1.56", STABILIZED_EXTRA_LINK_ARG),
"patch-in-config" => stabilized_warn(k, "1.56", STABILIZED_PATCH_IN_CONFIG),
+ "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES),
"future-incompat-report" => {
stabilized_warn(k, "1.59.0", STABILIZED_FUTURE_INCOMPAT_REPORT)
}
+ "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES),
"timings" => stabilized_warn(k, "1.60", STABILIZED_TIMINGS),
+ "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES),
+ "multitarget" => stabilized_warn(k, "1.64", STABILISED_MULTITARGET),
+ "sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY),
+ "terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH),
+ "doctest-in-workspace" => stabilized_warn(k, "1.72", STABILIZED_DOCTEST_IN_WORKSPACE),
+
+ // Unstable features
+ // Sorted alphabetically:
+ "advanced-env" => self.advanced_env = parse_empty(k, v)?,
+ "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
+ "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
+ "bindeps" => self.bindeps = parse_empty(k, v)?,
+ "build-std" => {
+ self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
+ }
+ "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(',')))?
+ }
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,
- "profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
- "msrv-policy" => self.msrv_policy = parse_empty(k, v)?,
+ "config-include" => self.config_include = parse_empty(k, v)?,
+ "credential-process" => self.credential_process = parse_empty(k, v)?,
+ "direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?,
+ "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
+ "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
+ "gitoxide" => {
+ self.gitoxide = v.map_or_else(
+ || Ok(Some(GitoxideFeatures::all())),
+ |v| parse_gitoxide(v.split(',')),
+ )?
+ }
+ "host-config" => self.host_config = parse_empty(k, v)?,
"lints" => self.lints = parse_empty(k, v)?,
+ "next-lockfile-bump" => self.next_lockfile_bump = parse_empty(k, v)?,
+ "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
+ "msrv-policy" => self.msrv_policy = parse_empty(k, v)?,
+ // can also be set in .cargo/config or with and ENV
+ "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
+ "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)?,
+ "publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
+ "registry-auth" => self.registry_auth = parse_empty(k, v)?,
+ "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
+ "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?,
+ "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
+ "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?,
+ "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),
}
diff --git a/src/tools/cargo/src/cargo/core/manifest.rs b/src/tools/cargo/src/cargo/core/manifest.rs
index 98498ead8..5d46a7e06 100644
--- a/src/tools/cargo/src/cargo/core/manifest.rs
+++ b/src/tools/cargo/src/cargo/core/manifest.rs
@@ -64,6 +64,7 @@ pub struct Manifest {
metabuild: Option<Vec<String>>,
resolve_behavior: Option<ResolveBehavior>,
lint_rustflags: Vec<String>,
+ embedded: bool,
}
/// When parsing `Cargo.toml`, some warnings should silenced
@@ -407,6 +408,7 @@ impl Manifest {
metabuild: Option<Vec<String>>,
resolve_behavior: Option<ResolveBehavior>,
lint_rustflags: Vec<String>,
+ embedded: bool,
) -> Manifest {
Manifest {
summary,
@@ -433,6 +435,7 @@ impl Manifest {
metabuild,
resolve_behavior,
lint_rustflags,
+ embedded,
}
}
@@ -500,6 +503,9 @@ impl Manifest {
pub fn links(&self) -> Option<&str> {
self.links.as_deref()
}
+ pub fn is_embedded(&self) -> bool {
+ self.embedded
+ }
pub fn workspace_config(&self) -> &WorkspaceConfig {
&self.workspace
diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs
index 40ba9cdf8..f4ab448d2 100644
--- a/src/tools/cargo/src/cargo/core/package.rs
+++ b/src/tools/cargo/src/cargo/core/package.rs
@@ -10,10 +10,10 @@ use std::time::{Duration, Instant};
use anyhow::Context;
use bytesize::ByteSize;
-use curl::easy::{Easy, HttpVersion};
+use curl::easy::Easy;
use curl::multi::{EasyHandle, Multi};
use lazycell::LazyCell;
-use log::{debug, warn};
+use log::debug;
use semver::Version;
use serde::Serialize;
@@ -24,10 +24,11 @@ use crate::core::resolver::{HasDevUnits, Resolve};
use crate::core::source::MaybePackage;
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
use crate::core::{SourceMap, Summary, Workspace};
-use crate::ops;
use crate::util::config::PackageCacheLock;
use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
use crate::util::interning::InternedString;
+use crate::util::network::http::http_handle_and_timeout;
+use crate::util::network::http::HttpTimeout;
use crate::util::network::retry::{Retry, RetryResult};
use crate::util::network::sleep::SleepTracker;
use crate::util::{self, internal, Config, Progress, ProgressStyle};
@@ -348,7 +349,7 @@ pub struct Downloads<'a, 'cfg> {
/// Note that timeout management is done manually here instead of in libcurl
/// because we want to apply timeouts to an entire batch of operations, not
/// any one particular single operation.
- timeout: ops::HttpTimeout,
+ timeout: HttpTimeout,
/// Last time bytes were received.
updated_at: Cell<Instant>,
/// This is a slow-speed check. It is reset to `now + timeout_duration`
@@ -441,7 +442,7 @@ impl<'cfg> PackageSet<'cfg> {
pub fn enable_download<'a>(&'a self) -> CargoResult<Downloads<'a, 'cfg>> {
assert!(!self.downloading.replace(true));
- let timeout = ops::HttpTimeout::new(self.config)?;
+ let timeout = HttpTimeout::new(self.config)?;
Ok(Downloads {
start: Instant::now(),
set: self,
@@ -713,7 +714,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
debug!("downloading {} as {}", id, token);
assert!(self.pending_ids.insert(id));
- let (mut handle, _timeout) = ops::http_handle_and_timeout(self.set.config)?;
+ let (mut handle, _timeout) = http_handle_and_timeout(self.set.config)?;
handle.get(true)?;
handle.url(&url)?;
handle.follow_location(true)?; // follow redirects
@@ -725,32 +726,8 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
handle.http_headers(headers)?;
}
- // Enable HTTP/2 to be used as it'll allow true multiplexing which makes
- // downloads much faster.
- //
- // Currently Cargo requests the `http2` feature of the `curl` crate
- // which means it should always be built in. On OSX, however, we ship
- // cargo still linked against the system libcurl. Building curl with
- // ALPN support for HTTP/2 requires newer versions of OSX (the
- // SecureTransport API) than we want to ship Cargo for. By linking Cargo
- // against the system libcurl then older curl installations won't use
- // HTTP/2 but newer ones will. All that to basically say we ignore
- // errors here on OSX, but consider this a fatal error to not activate
- // HTTP/2 on all other platforms.
- if self.set.multiplexing {
- crate::try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2");
- } else {
- handle.http_version(HttpVersion::V11)?;
- }
-
- // This is an option to `libcurl` which indicates that if there's a
- // bunch of parallel requests to the same host they all wait until the
- // pipelining status of the host is known. This means that we won't
- // initiate dozens of connections to crates.io, but rather only one.
- // Once the main one is opened we realized that pipelining is possible
- // and multiplexing is possible with static.crates.io. All in all this
- // reduces the number of connections down to a more manageable state.
- crate::try_old_curl!(handle.pipewait(true), "pipewait");
+ // Enable HTTP/2 if possible.
+ crate::try_old_curl_http2_pipewait!(self.set.multiplexing, handle);
handle.write_function(move |buf| {
debug!("{} - {} bytes of data", token, buf.len());
diff --git a/src/tools/cargo/src/cargo/core/package_id.rs b/src/tools/cargo/src/cargo/core/package_id.rs
index ee31e9c48..e17a73e68 100644
--- a/src/tools/cargo/src/cargo/core/package_id.rs
+++ b/src/tools/cargo/src/cargo/core/package_id.rs
@@ -5,6 +5,7 @@ use std::hash::Hash;
use std::path::Path;
use std::ptr;
use std::sync::Mutex;
+use std::sync::OnceLock;
use serde::de;
use serde::ser;
@@ -13,10 +14,7 @@ use crate::core::source::SourceId;
use crate::util::interning::InternedString;
use crate::util::{CargoResult, ToSemver};
-lazy_static::lazy_static! {
- static ref PACKAGE_ID_CACHE: Mutex<HashSet<&'static PackageIdInner>> =
- Mutex::new(HashSet::new());
-}
+static PACKAGE_ID_CACHE: OnceLock<Mutex<HashSet<&'static PackageIdInner>>> = OnceLock::new();
/// Identifier for a specific version of a package in a specific source.
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
@@ -147,7 +145,10 @@ impl PackageId {
version,
source_id,
};
- let mut cache = PACKAGE_ID_CACHE.lock().unwrap();
+ let mut cache = PACKAGE_ID_CACHE
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
let inner = cache.get(&inner).cloned().unwrap_or_else(|| {
let inner = Box::leak(Box::new(inner));
cache.insert(inner);
@@ -195,6 +196,11 @@ impl PackageId {
pub fn stable_hash(self, workspace: &Path) -> PackageIdStableHash<'_> {
PackageIdStableHash(self, workspace)
}
+
+ /// Filename of the `.crate` tarball, e.g., `once_cell-1.18.0.crate`.
+ pub fn tarball_name(&self) -> String {
+ format!("{}-{}.crate", self.name(), self.version())
+ }
}
pub struct PackageIdStableHash<'a>(PackageId, &'a Path);
diff --git a/src/tools/cargo/src/cargo/core/profiles.rs b/src/tools/cargo/src/cargo/core/profiles.rs
index 3831f18c2..5c7d3e248 100644
--- a/src/tools/cargo/src/cargo/core/profiles.rs
+++ b/src/tools/cargo/src/cargo/core/profiles.rs
@@ -1,4 +1,4 @@
-//! # Profiles: built-in and customizable compiler flag presets
+//! Handles built-in and customizable compiler flag presets.
//!
//! [`Profiles`] is a collections of built-in profiles, and profiles defined
//! in the root manifest and configurations.
@@ -449,9 +449,7 @@ impl ProfileMaker {
// a unit is shared. If that's the case, we'll use the deferred value
// below so the unit can be reused, otherwise we can avoid emitting
// the unit's debuginfo.
- if let Some(debuginfo) = profile.debuginfo.to_option() {
- profile.debuginfo = DebugInfo::Deferred(debuginfo);
- }
+ profile.debuginfo = DebugInfo::Deferred(profile.debuginfo.into_inner());
}
// ... and next comes any other sorts of overrides specified in
// profiles, such as `[profile.release.build-override]` or
@@ -529,7 +527,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
profile.codegen_units = toml.codegen_units;
}
if let Some(debuginfo) = toml.debug {
- profile.debuginfo = DebugInfo::Explicit(debuginfo);
+ profile.debuginfo = DebugInfo::Resolved(debuginfo);
}
if let Some(debug_assertions) = toml.debug_assertions {
profile.debug_assertions = debug_assertions;
@@ -611,7 +609,7 @@ impl Default for Profile {
lto: Lto::Bool(false),
codegen_backend: None,
codegen_units: None,
- debuginfo: DebugInfo::None,
+ debuginfo: DebugInfo::Resolved(TomlDebugInfo::None),
debug_assertions: false,
split_debuginfo: None,
overflow_checks: false,
@@ -680,7 +678,7 @@ impl Profile {
Profile {
name: InternedString::new("dev"),
root: ProfileRoot::Debug,
- debuginfo: DebugInfo::Explicit(TomlDebugInfo::Full),
+ debuginfo: DebugInfo::Resolved(TomlDebugInfo::Full),
debug_assertions: true,
overflow_checks: true,
incremental: true,
@@ -720,11 +718,8 @@ impl Profile {
/// The debuginfo level setting.
///
-/// This is semantically an `Option<u32>`, and should be used as so via the
-/// [DebugInfo::to_option] method for all intents and purposes:
-/// - `DebugInfo::None` corresponds to `None`
-/// - `DebugInfo::Explicit(u32)` and `DebugInfo::Deferred` correspond to
-/// `Option<u32>::Some`
+/// This is semantically a [`TomlDebugInfo`], and should be used as so via the
+/// [`DebugInfo::into_inner`] method for all intents and purposes.
///
/// Internally, it's used to model a debuginfo level whose value can be deferred
/// for optimization purposes: host dependencies usually don't need the same
@@ -736,35 +731,34 @@ impl Profile {
#[derive(Debug, Copy, Clone, serde::Serialize)]
#[serde(untagged)]
pub enum DebugInfo {
- /// No debuginfo level was set.
- None,
- /// A debuginfo level that is explicitly set, by a profile or a user.
- Explicit(TomlDebugInfo),
+ /// A debuginfo level that is fixed and will not change.
+ ///
+ /// This can be set by a profile, user, or default value.
+ Resolved(TomlDebugInfo),
/// For internal purposes: a deferred debuginfo level that can be optimized
/// away, but has this value otherwise.
///
- /// Behaves like `Explicit` in all situations except for the default build
+ /// Behaves like `Resolved` in all situations except for the default build
/// dependencies profile: whenever a build dependency is not shared with
/// runtime dependencies, this level is weakened to a lower level that is
- /// faster to build (see [DebugInfo::weaken]).
+ /// faster to build (see [`DebugInfo::weaken`]).
///
/// In all other situations, this level value will be the one to use.
Deferred(TomlDebugInfo),
}
impl DebugInfo {
- /// The main way to interact with this debuginfo level, turning it into an Option.
- pub fn to_option(self) -> Option<TomlDebugInfo> {
+ /// The main way to interact with this debuginfo level, turning it into a [`TomlDebugInfo`].
+ pub fn into_inner(self) -> TomlDebugInfo {
match self {
- DebugInfo::None => None,
- DebugInfo::Explicit(v) | DebugInfo::Deferred(v) => Some(v),
+ DebugInfo::Resolved(v) | DebugInfo::Deferred(v) => v,
}
}
/// Returns true if any debuginfo will be generated. Helper
/// for a common operation on the usual `Option` representation.
pub(crate) fn is_turned_on(&self) -> bool {
- !matches!(self.to_option(), None | Some(TomlDebugInfo::None))
+ !matches!(self.into_inner(), TomlDebugInfo::None)
}
pub(crate) fn is_deferred(&self) -> bool {
@@ -774,24 +768,20 @@ impl DebugInfo {
/// Force the deferred, preferred, debuginfo level to a finalized explicit value.
pub(crate) fn finalize(self) -> Self {
match self {
- DebugInfo::Deferred(v) => DebugInfo::Explicit(v),
+ DebugInfo::Deferred(v) => DebugInfo::Resolved(v),
_ => self,
}
}
/// Reset to the lowest level: no debuginfo.
- /// If it is explicitly set, keep it explicit.
pub(crate) fn weaken(self) -> Self {
- match self {
- DebugInfo::None => DebugInfo::None,
- _ => DebugInfo::Explicit(TomlDebugInfo::None),
- }
+ DebugInfo::Resolved(TomlDebugInfo::None)
}
}
impl PartialEq for DebugInfo {
fn eq(&self, other: &DebugInfo) -> bool {
- self.to_option().eq(&other.to_option())
+ self.into_inner().eq(&other.into_inner())
}
}
@@ -799,19 +789,19 @@ impl Eq for DebugInfo {}
impl Hash for DebugInfo {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- self.to_option().hash(state);
+ self.into_inner().hash(state);
}
}
impl PartialOrd for DebugInfo {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- self.to_option().partial_cmp(&other.to_option())
+ self.into_inner().partial_cmp(&other.into_inner())
}
}
impl Ord for DebugInfo {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.to_option().cmp(&other.to_option())
+ self.into_inner().cmp(&other.into_inner())
}
}
diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs
index 88d0d8296..f73d023b1 100644
--- a/src/tools/cargo/src/cargo/core/resolver/encode.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs
@@ -154,10 +154,18 @@ impl EncodableResolve {
/// primary uses is to be used with `resolve_with_previous` to guide the
/// resolver to create a complete Resolve.
pub fn into_resolve(self, original: &str, ws: &Workspace<'_>) -> CargoResult<Resolve> {
+ let unstable_lockfile_version_allowed = ws.config().cli_unstable().next_lockfile_bump;
let path_deps = build_path_deps(ws)?;
let mut checksums = HashMap::new();
let mut version = match self.version {
+ Some(4) if ws.config().nightly_features_allowed => {
+ if unstable_lockfile_version_allowed {
+ ResolveVersion::V4
+ } else {
+ anyhow::bail!("lock file version 4 requires `-Znext-lockfile-bump`");
+ }
+ }
Some(3) => ResolveVersion::V3,
Some(n) => bail!(
"lock file version `{}` was found, but this version of Cargo \
@@ -612,6 +620,7 @@ impl ser::Serialize for Resolve {
metadata,
patch,
version: match self.version() {
+ ResolveVersion::V4 => Some(4),
ResolveVersion::V3 => Some(3),
ResolveVersion::V2 | ResolveVersion::V1 => None,
},
diff --git a/src/tools/cargo/src/cargo/core/resolver/features.rs b/src/tools/cargo/src/cargo/core/resolver/features.rs
index 6b79722ca..3670e8711 100644
--- a/src/tools/cargo/src/cargo/core/resolver/features.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/features.rs
@@ -1,4 +1,4 @@
-//! # Feature resolver
+//! Resolves conditional compilation for [`features` section] in the manifest.
//!
//! This is a [new feature resolver] that runs independently of the main
//! dependency resolver. It has several options which can enable new feature
@@ -34,6 +34,7 @@
//!
//! There are probably other assumptions that I am forgetting.
//!
+//! [`features` section]: https://doc.rust-lang.org/nightly/cargo/reference/features.html
//! [new feature resolver]: https://doc.rust-lang.org/nightly/cargo/reference/resolver.html#feature-resolver-version-2
//! [`resolve_ws_with_opts`]: crate::ops::resolve_ws_with_opts
diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
index 6ab957e92..8405a1245 100644
--- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
@@ -80,6 +80,10 @@ pub enum ResolveVersion {
/// V3 by default staring in 1.53.
#[default]
V3,
+ /// Unstable. Will collect a certain amount of changes and then go.
+ ///
+ /// Changes made:
+ V4,
}
impl Resolve {
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 002f11ff8..bf26d0498 100644
--- a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
@@ -81,7 +81,6 @@ impl VersionPreferences {
mod test {
use super::*;
use crate::core::SourceId;
- use crate::util::Config;
use std::collections::BTreeMap;
fn pkgid(name: &str, version: &str) -> PackageId {
@@ -98,10 +97,8 @@ mod test {
fn summ(name: &str, version: &str) -> Summary {
let pkg_id = pkgid(name, version);
- let config = Config::default().unwrap();
let features = BTreeMap::new();
Summary::new(
- &config,
pkg_id,
Vec::new(),
&features,
diff --git a/src/tools/cargo/src/cargo/core/shell.rs b/src/tools/cargo/src/cargo/core/shell.rs
index f74bde257..4d45e6098 100644
--- a/src/tools/cargo/src/cargo/core/shell.rs
+++ b/src/tools/cargo/src/cargo/core/shell.rs
@@ -1,7 +1,7 @@
use std::fmt;
use std::io::prelude::*;
+use std::io::IsTerminal;
-use is_terminal::IsTerminal;
use termcolor::Color::{Cyan, Green, Red, Yellow};
use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};
diff --git a/src/tools/cargo/src/cargo/core/source/mod.rs b/src/tools/cargo/src/cargo/core/source/mod.rs
index 6ca614d34..2d1db1d53 100644
--- a/src/tools/cargo/src/cargo/core/source/mod.rs
+++ b/src/tools/cargo/src/cargo/core/source/mod.rs
@@ -87,10 +87,25 @@ pub trait Source {
/// If quiet, the source should not display any progress or status messages.
fn set_quiet(&mut self, quiet: bool);
- /// Fetches the full package for each name and version specified.
+ /// Starts the process to fetch a [`Package`] for the given [`PackageId`].
+ ///
+ /// If the source already has the package available on disk, then it
+ /// should return immediately with [`MaybePackage::Ready`] with the
+ /// [`Package`]. Otherwise it should return a [`MaybePackage::Download`]
+ /// to indicate the URL to download the package (this is for remote
+ /// registry sources only).
+ ///
+ /// In the case where [`MaybePackage::Download`] is returned, then the
+ /// package downloader will call [`Source::finish_download`] after the
+ /// download has finished.
fn download(&mut self, package: PackageId) -> CargoResult<MaybePackage>;
- /// Fetches the full package **immediately** for each name and version specified.
+ /// Convenience method used to **immediately** fetch a [`Package`] for the
+ /// given [`PackageId`].
+ ///
+ /// This may trigger a download if necessary. This should only be used
+ /// when a single package is needed (as in the case for `cargo install`).
+ /// Otherwise downloads should be batched together via [`PackageSet`].
fn download_now(self: Box<Self>, package: PackageId, config: &Config) -> CargoResult<Package>
where
Self: std::marker::Sized,
@@ -102,7 +117,13 @@ pub trait Source {
Ok(Package::clone(pkg))
}
- /// Finalizes the download contents of the given [`PackageId`] to a [`Package`].
+ /// Gives the source the downloaded `.crate` file.
+ ///
+ /// When a source has returned [`MaybePackage::Download`] in the
+ /// [`Source::download`] method, then this function will be called with
+ /// the results of the download of the given URL. The source is
+ /// responsible for saving to disk, and returning the appropriate
+ /// [`Package`].
fn finish_download(&mut self, pkg_id: PackageId, contents: Vec<u8>) -> CargoResult<Package>;
/// Generates a unique string which represents the fingerprint of the
diff --git a/src/tools/cargo/src/cargo/core/source/source_id.rs b/src/tools/cargo/src/cargo/core/source/source_id.rs
index c369dab16..4064364d5 100644
--- a/src/tools/cargo/src/cargo/core/source/source_id.rs
+++ b/src/tools/cargo/src/cargo/core/source/source_id.rs
@@ -13,11 +13,10 @@ use std::hash::{self, Hash};
use std::path::{Path, PathBuf};
use std::ptr;
use std::sync::Mutex;
+use std::sync::OnceLock;
use url::Url;
-lazy_static::lazy_static! {
- static ref SOURCE_ID_CACHE: Mutex<HashSet<&'static SourceIdInner>> = Default::default();
-}
+static SOURCE_ID_CACHE: OnceLock<Mutex<HashSet<&'static SourceIdInner>>> = OnceLock::new();
/// Unique identifier for a source of packages.
///
@@ -118,7 +117,10 @@ impl SourceId {
/// Interns the value and returns the wrapped type.
fn wrap(inner: SourceIdInner) -> SourceId {
- let mut cache = SOURCE_ID_CACHE.lock().unwrap();
+ let mut cache = SOURCE_ID_CACHE
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
let inner = cache.get(&inner).cloned().unwrap_or_else(|| {
let inner = Box::leak(Box::new(inner));
cache.insert(inner);
diff --git a/src/tools/cargo/src/cargo/core/summary.rs b/src/tools/cargo/src/cargo/core/summary.rs
index 2535c4482..1883df33b 100644
--- a/src/tools/cargo/src/cargo/core/summary.rs
+++ b/src/tools/cargo/src/cargo/core/summary.rs
@@ -1,6 +1,6 @@
use crate::core::{Dependency, PackageId, SourceId};
use crate::util::interning::InternedString;
-use crate::util::{CargoResult, Config};
+use crate::util::CargoResult;
use anyhow::bail;
use semver::Version;
use std::collections::{BTreeMap, HashMap, HashSet};
@@ -30,7 +30,6 @@ struct Inner {
impl Summary {
pub fn new(
- config: &Config,
pkg_id: PackageId,
dependencies: Vec<Dependency>,
features: &BTreeMap<InternedString, Vec<InternedString>>,
@@ -49,7 +48,7 @@ impl Summary {
)
}
}
- let feature_map = build_feature_map(config, pkg_id, features, &dependencies)?;
+ let feature_map = build_feature_map(pkg_id, features, &dependencies)?;
Ok(Summary {
inner: Rc::new(Inner {
package_id: pkg_id,
@@ -140,7 +139,6 @@ impl Hash for Summary {
/// Checks features for errors, bailing out a CargoResult:Err if invalid,
/// and creates FeatureValues for each feature.
fn build_feature_map(
- config: &Config,
pkg_id: PackageId,
features: &BTreeMap<InternedString, Vec<InternedString>>,
dependencies: &[Dependency],
@@ -204,7 +202,7 @@ fn build_feature_map(
feature
);
}
- validate_feature_name(config, pkg_id, feature)?;
+ validate_feature_name(pkg_id, feature)?;
for fv in fvs {
// Find data for the referenced dependency...
let dep_data = {
@@ -431,33 +429,63 @@ impl fmt::Display for FeatureValue {
pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;
-fn validate_feature_name(config: &Config, pkg_id: PackageId, name: &str) -> CargoResult<()> {
+fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> {
let mut chars = name.chars();
- const FUTURE: &str = "This was previously accepted but is being phased out; \
- it will become a hard error in a future release.\n\
- For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, \
- and please leave a comment if this will be a problem for your project.";
if let Some(ch) = chars.next() {
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
- config.shell().warn(&format!(
+ bail!(
"invalid character `{}` in feature `{}` in package {}, \
the first character must be a Unicode XID start character or digit \
- (most letters or `_` or `0` to `9`)\n\
- {}",
- ch, name, pkg_id, FUTURE
- ))?;
+ (most letters or `_` or `0` to `9`)",
+ ch,
+ name,
+ pkg_id
+ );
}
}
for ch in chars {
if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') {
- config.shell().warn(&format!(
+ bail!(
"invalid character `{}` in feature `{}` in package {}, \
characters must be Unicode XID characters, `+`, or `.` \
- (numbers, `+`, `-`, `_`, `.`, or most letters)\n\
- {}",
- ch, name, pkg_id, FUTURE
- ))?;
+ (numbers, `+`, `-`, `_`, `.`, or most letters)",
+ ch,
+ name,
+ pkg_id
+ );
}
}
Ok(())
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::sources::CRATES_IO_INDEX;
+ use crate::util::into_url::IntoUrl;
+
+ use crate::core::SourceId;
+
+ #[test]
+ fn valid_feature_names() {
+ let loc = CRATES_IO_INDEX.into_url().unwrap();
+ let source_id = SourceId::for_registry(&loc).unwrap();
+ let pkg_id = PackageId::new("foo", "1.0.0", source_id).unwrap();
+
+ assert!(validate_feature_name(pkg_id, "c++17").is_ok());
+ assert!(validate_feature_name(pkg_id, "128bit").is_ok());
+ assert!(validate_feature_name(pkg_id, "_foo").is_ok());
+ assert!(validate_feature_name(pkg_id, "feat-name").is_ok());
+ assert!(validate_feature_name(pkg_id, "feat_name").is_ok());
+ assert!(validate_feature_name(pkg_id, "foo.bar").is_ok());
+
+ assert!(validate_feature_name(pkg_id, "+foo").is_err());
+ assert!(validate_feature_name(pkg_id, "-foo").is_err());
+ assert!(validate_feature_name(pkg_id, ".foo").is_err());
+ assert!(validate_feature_name(pkg_id, "foo:bar").is_err());
+ assert!(validate_feature_name(pkg_id, "foo?").is_err());
+ 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());
+ }
+}
diff --git a/src/tools/cargo/src/cargo/core/workspace.rs b/src/tools/cargo/src/cargo/core/workspace.rs
index 9922d6d33..db9c18010 100644
--- a/src/tools/cargo/src/cargo/core/workspace.rs
+++ b/src/tools/cargo/src/cargo/core/workspace.rs
@@ -15,7 +15,7 @@ use crate::core::features::Features;
use crate::core::registry::PackageRegistry;
use crate::core::resolver::features::CliFeatures;
use crate::core::resolver::ResolveBehavior;
-use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec};
+use crate::core::{Dependency, Edition, FeatureValue, PackageId, PackageIdSpec};
use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
use crate::ops;
use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
@@ -381,7 +381,21 @@ impl<'cfg> Workspace<'cfg> {
pub fn target_dir(&self) -> Filesystem {
self.target_dir
.clone()
- .unwrap_or_else(|| Filesystem::new(self.root().join("target")))
+ .unwrap_or_else(|| self.default_target_dir())
+ }
+
+ fn default_target_dir(&self) -> Filesystem {
+ if self.root_maybe().is_embedded() {
+ let hash = crate::util::hex::short_hash(&self.root_manifest().to_string_lossy());
+ let mut rel_path = PathBuf::new();
+ rel_path.push("target");
+ rel_path.push(&hash[0..2]);
+ rel_path.push(&hash[2..]);
+
+ self.config().home().join(rel_path)
+ } else {
+ Filesystem::new(self.root().join("target"))
+ }
}
/// Returns the root `[replace]` section of this workspace.
@@ -726,6 +740,10 @@ impl<'cfg> Workspace<'cfg> {
if self.members.contains(&manifest_path) {
return Ok(());
}
+ if is_path_dep && self.root_maybe().is_embedded() {
+ // Embedded manifests cannot have workspace members
+ return Ok(());
+ }
if is_path_dep
&& !manifest_path.parent().unwrap().starts_with(self.root())
&& self.find_root(&manifest_path)? != self.root_manifest
@@ -993,6 +1011,24 @@ impl<'cfg> Workspace<'cfg> {
}
}
}
+ if let MaybePackage::Virtual(vm) = self.root_maybe() {
+ if vm.resolve_behavior().is_none() {
+ if let Some(edition) = self
+ .members()
+ .filter(|p| p.manifest_path() != root_manifest)
+ .map(|p| p.manifest().edition())
+ .filter(|&e| e >= Edition::Edition2021)
+ .max()
+ {
+ let resolver = edition.default_resolve_behavior().to_manifest();
+ self.config.shell().warn(format_args!("some crates are on edition {edition} which defaults to `resolver = \"{resolver}\"`, but virtual workspaces default to `resolver = \"1\"`"))?;
+ self.config.shell().note(
+ "to keep the current resolver, specify `workspace.resolver = \"1\"` in the workspace root's manifest",
+ )?;
+ self.config.shell().note(format_args!("to use the edition {edition} resolver, specify `workspace.resolver = \"{resolver}\"` in the workspace root's manifest"))?;
+ }
+ }
+ }
}
Ok(())
}
@@ -1562,6 +1598,14 @@ impl MaybePackage {
MaybePackage::Virtual(ref vm) => vm.workspace_config(),
}
}
+
+ /// Has an embedded manifest (single-file package)
+ pub fn is_embedded(&self) -> bool {
+ match self {
+ MaybePackage::Package(p) => p.manifest().is_embedded(),
+ MaybePackage::Virtual(_) => false,
+ }
+ }
}
impl WorkspaceRootConfig {
diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs
index 31d03ad25..a03d51199 100644
--- a/src/tools/cargo/src/cargo/lib.rs
+++ b/src/tools/cargo/src/cargo/lib.rs
@@ -6,6 +6,9 @@
#![allow(clippy::all)]
#![warn(clippy::disallowed_methods)]
#![warn(clippy::self_named_module_files)]
+#![warn(clippy::print_stdout)]
+#![warn(clippy::print_stderr)]
+#![warn(clippy::dbg_macro)]
#![allow(rustdoc::private_intra_doc_links)]
//! # Cargo as a library
diff --git a/src/tools/cargo/src/cargo/ops/cargo_clean.rs b/src/tools/cargo/src/cargo/ops/cargo_clean.rs
index de3139c99..b9b33690e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_clean.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_clean.rs
@@ -297,7 +297,12 @@ fn rm_rf(path: &Path, config: &Config, progress: &mut dyn CleaningProgressBar) -
let entry = entry?;
progress.on_clean()?;
if entry.file_type().is_dir() {
- paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?;
+ // The contents should have been removed by now, but sometimes a race condition is hit
+ // where other files have been added by the OS. `paths::remove_dir_all` also falls back
+ // to `std::fs::remove_dir_all`, which may be more reliable than a simple walk in
+ // platform-specific edge cases.
+ paths::remove_dir_all(entry.path())
+ .with_context(|| "could not remove build directory")?;
} else {
paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?;
}
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 3b6043d4f..f53a9e934 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
@@ -1,7 +1,5 @@
-//! # The Cargo "compile" operation
-//!
-//! This module contains the entry point for starting the compilation process
-//! for commands like `build`, `test`, `doc`, `rustc`, etc.
+//! The entry point for starting the compilation process for commands like
+//! `build`, `test`, `doc`, `rustc`, etc.
//!
//! The [`compile`] function will do all the work to compile a workspace. A
//! rough outline is:
diff --git a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
index bac3f0278..273bce284 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
@@ -2,6 +2,7 @@ use crate::core::compiler::standard_lib;
use crate::core::compiler::{BuildConfig, CompileMode, RustcTargetData};
use crate::core::{PackageSet, Resolve, Workspace};
use crate::ops;
+use crate::util::config::JobsConfig;
use crate::util::CargoResult;
use crate::util::Config;
use std::collections::HashSet;
@@ -20,7 +21,7 @@ pub fn fetch<'a>(
ws.emit_warnings()?;
let (mut packages, resolve) = ops::resolve_ws(ws)?;
- let jobs = Some(1);
+ let jobs = Some(JobsConfig::Integer(1));
let keep_going = false;
let config = ws.config();
let build_config = BuildConfig::new(
diff --git a/src/tools/cargo/src/cargo/ops/cargo_install.rs b/src/tools/cargo/src/cargo/ops/cargo_install.rs
index 5f843e8c7..8ddfa4fab 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_install.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_install.rs
@@ -320,7 +320,9 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
format!(
"failed to compile `{}`, intermediate artifacts can be \
- found at `{}`",
+ found at `{}`.\nTo reuse those artifacts with a future \
+ compilation, set the environment variable \
+ `CARGO_TARGET_DIR` to that path.",
self.pkg,
self.ws.target_dir().display()
)
diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs
index 697798c0c..b113671b0 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_new.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs
@@ -163,6 +163,7 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions) -> CargoResult<&'a str> {
})
}
+/// See also `util::toml::embedded::sanitize_name`
fn check_name(
name: &str,
show_name_help: bool,
@@ -820,6 +821,18 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
workspace_package_keys,
)
}
+
+ // Try to inherit the workspace lints key if it exists.
+ if config.cli_unstable().lints
+ && workspace_document
+ .get("workspace")
+ .and_then(|workspace| workspace.get("lints"))
+ .is_some()
+ {
+ let mut table = toml_edit::Table::new();
+ table["workspace"] = toml_edit::value(true);
+ manifest["lints"] = toml_edit::Item::Table(table);
+ }
}
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs
index f80848c75..a322afbb3 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_package.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs
@@ -13,6 +13,7 @@ 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::config::JobsConfig;
use crate::util::errors::CargoResult;
use crate::util::toml::TomlManifest;
use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock};
@@ -31,7 +32,7 @@ pub struct PackageOpts<'cfg> {
pub check_metadata: bool,
pub allow_dirty: bool,
pub verify: bool,
- pub jobs: Option<i32>,
+ pub jobs: Option<JobsConfig>,
pub keep_going: bool,
pub to_package: ops::Packages,
pub targets: Vec<String>,
@@ -126,7 +127,7 @@ pub fn package_one(
super::check_dep_has_version(dep, false)?;
}
- let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
+ let filename = pkg.package_id().tarball_name();
let dir = ws.target_dir().join("package");
let mut dst = {
let tmp = format!(".{}", filename);
@@ -198,7 +199,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
check_metadata: opts.check_metadata,
allow_dirty: opts.allow_dirty,
verify: opts.verify,
- jobs: opts.jobs,
+ jobs: opts.jobs.clone(),
keep_going: opts.keep_going,
to_package: ops::Packages::Default,
targets: opts.targets.clone(),
@@ -392,7 +393,7 @@ fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
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, 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());
// Regenerate Cargo.lock using the old one as a guide.
@@ -861,7 +862,7 @@ fn run_verify(
&ops::CompileOptions {
build_config: BuildConfig::new(
config,
- opts.jobs,
+ opts.jobs.clone(),
opts.keep_going,
&opts.targets,
CompileMode::Build,
diff --git a/src/tools/cargo/src/cargo/ops/cargo_test.rs b/src/tools/cargo/src/cargo/ops/cargo_test.rs
index b7e61982d..1ddf7755f 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_test.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_test.rs
@@ -172,7 +172,6 @@ fn run_doc_tests(
let config = ws.config();
let mut errors = Vec::new();
let doctest_xcompile = config.cli_unstable().doctest_xcompile;
- let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;
for doctest_info in &compilation.to_doc_test {
let Doctest {
@@ -215,15 +214,9 @@ fn run_doc_tests(
p.arg("--crate-name").arg(&unit.target.crate_name());
p.arg("--test");
- if doctest_in_workspace {
- add_path_args(ws, unit, &mut p);
- // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
- p.arg("-Z").arg("unstable-options");
- p.arg("--test-run-directory")
- .arg(unit.pkg.root().to_path_buf());
- } else {
- p.arg(unit.target.src_path().path().unwrap());
- }
+ add_path_args(ws, unit, &mut p);
+ p.arg("--test-run-directory")
+ .arg(unit.pkg.root().to_path_buf());
if let CompileKind::Target(target) = unit.kind {
// use `rustc_target()` to properly handle JSON target paths
diff --git a/src/tools/cargo/src/cargo/ops/lockfile.rs b/src/tools/cargo/src/cargo/ops/lockfile.rs
index e11e492af..b27a7e742 100644
--- a/src/tools/cargo/src/cargo/ops/lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/lockfile.rs
@@ -8,12 +8,12 @@ use crate::util::Filesystem;
use anyhow::Context as _;
pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
- if !ws.root().join("Cargo.lock").exists() {
+ let lock_root = lock_root(ws);
+ if !lock_root.as_path_unlocked().join("Cargo.lock").exists() {
return Ok(None);
}
- let root = Filesystem::new(ws.root().to_path_buf());
- let mut f = root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?;
+ let mut f = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?;
let mut s = String::new();
f.read_to_string(&mut s)
@@ -30,12 +30,12 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
/// Generate a toml String of Cargo.lock from a Resolve.
pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<String> {
- let (_orig, out, _ws_root) = resolve_to_string_orig(ws, resolve);
+ let (_orig, out, _lock_root) = resolve_to_string_orig(ws, resolve);
Ok(out)
}
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<()> {
- let (orig, mut out, ws_root) = resolve_to_string_orig(ws, resolve);
+ let (orig, mut out, lock_root) = resolve_to_string_orig(ws, resolve);
// If the lock file contents haven't changed so don't rewrite it. This is
// helpful on read-only filesystems.
@@ -55,7 +55,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
"the lock file {} needs to be updated but {} was passed to prevent this\n\
If you want to try to generate the lock file without accessing the network, \
remove the {} flag and use --offline instead.",
- ws.root().to_path_buf().join("Cargo.lock").display(),
+ lock_root.as_path_unlocked().join("Cargo.lock").display(),
flag,
flag
);
@@ -69,17 +69,30 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
if resolve.version() < ResolveVersion::default() {
resolve.set_version(ResolveVersion::default());
out = serialize_resolve(resolve, orig.as_deref());
+ } else if resolve.version() > ResolveVersion::default()
+ && !ws.config().cli_unstable().next_lockfile_bump
+ {
+ // The next version hasn't yet stabilized.
+ anyhow::bail!(
+ "lock file version `{:?}` requires `-Znext-lockfile-bump`",
+ resolve.version()
+ )
}
// Ok, if that didn't work just write it out
- ws_root
+ lock_root
.open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
.and_then(|mut f| {
f.file().set_len(0)?;
f.write_all(out.as_bytes())?;
Ok(())
})
- .with_context(|| format!("failed to write {}", ws.root().join("Cargo.lock").display()))?;
+ .with_context(|| {
+ format!(
+ "failed to write {}",
+ lock_root.as_path_unlocked().join("Cargo.lock").display()
+ )
+ })?;
Ok(())
}
@@ -88,15 +101,15 @@ fn resolve_to_string_orig(
resolve: &mut Resolve,
) -> (Option<String>, String, Filesystem) {
// Load the original lock file if it exists.
- let ws_root = Filesystem::new(ws.root().to_path_buf());
- let orig = ws_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
+ let lock_root = lock_root(ws);
+ let orig = lock_root.open_ro("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)?;
Ok(s)
});
let out = serialize_resolve(resolve, orig.as_deref().ok());
- (orig.ok(), out, ws_root)
+ (orig.ok(), out, lock_root)
}
fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String {
@@ -227,3 +240,11 @@ fn emit_package(dep: &toml::Table, out: &mut String) {
out.push_str(&format!("replace = {}\n\n", &dep["replace"]));
}
}
+
+fn lock_root(ws: &Workspace<'_>) -> Filesystem {
+ if ws.root_maybe().is_embedded() {
+ ws.target_dir()
+ } else {
+ Filesystem::new(ws.root().to_owned())
+ }
+}
diff --git a/src/tools/cargo/src/cargo/ops/mod.rs b/src/tools/cargo/src/cargo/ops/mod.rs
index 4b6aea991..d4ec442dd 100644
--- a/src/tools/cargo/src/cargo/ops/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/mod.rs
@@ -21,11 +21,15 @@ pub use self::cargo_test::{run_benches, run_tests, TestOptions};
pub use self::cargo_uninstall::uninstall;
pub use self::fix::{fix, fix_exec_rustc, fix_get_proxy_lock_addr, FixOptions};
pub use self::lockfile::{load_pkg_lockfile, resolve_to_string, write_pkg_lockfile};
-pub use self::registry::HttpTimeout;
-pub use self::registry::{configure_http_handle, http_handle, http_handle_and_timeout};
-pub use self::registry::{modify_owners, yank, OwnersOptions, PublishOpts};
-pub use self::registry::{needs_custom_http_transport, registry_login, registry_logout, search};
-pub use self::registry::{publish, RegistryCredentialConfig};
+pub use self::registry::modify_owners;
+pub use self::registry::publish;
+pub use self::registry::registry_login;
+pub use self::registry::registry_logout;
+pub use self::registry::search;
+pub use self::registry::yank;
+pub use self::registry::OwnersOptions;
+pub use self::registry::PublishOpts;
+pub use self::registry::RegistryCredentialConfig;
pub use self::resolve::{
add_overrides, get_resolved_packages, resolve_with_previous, resolve_ws, resolve_ws_with_opts,
WorkspaceResolve,
diff --git a/src/tools/cargo/src/cargo/ops/registry.rs b/src/tools/cargo/src/cargo/ops/registry.rs
deleted file mode 100644
index a8efb5492..000000000
--- a/src/tools/cargo/src/cargo/ops/registry.rs
+++ /dev/null
@@ -1,1227 +0,0 @@
-use std::cmp;
-use std::collections::{BTreeMap, HashSet};
-use std::fs::File;
-use std::io::{self, BufRead};
-use std::iter::repeat;
-use std::path::PathBuf;
-use std::str;
-use std::task::Poll;
-use std::time::Duration;
-
-use anyhow::{anyhow, bail, format_err, Context as _};
-use cargo_util::paths;
-use crates_io::{self, NewCrate, NewCrateDependency, Registry};
-use curl::easy::{Easy, InfoType, SslOpt, SslVersion};
-use log::{log, Level};
-use pasetors::keys::{AsymmetricKeyPair, Generate};
-use pasetors::paserk::FormatAsPaserk;
-use termcolor::Color::Green;
-use termcolor::ColorSpec;
-use url::Url;
-
-use crate::core::dependency::DepKind;
-use crate::core::dependency::Dependency;
-use crate::core::manifest::ManifestMetadata;
-use crate::core::resolver::CliFeatures;
-use crate::core::source::Source;
-use crate::core::QueryKind;
-use crate::core::{Package, SourceId, Workspace};
-use crate::ops;
-use crate::ops::Packages;
-use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN, CRATES_IO_REGISTRY};
-use crate::util::auth::{
- paserk_public_from_paserk_secret, Secret, {self, AuthorizationError},
-};
-use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange};
-use crate::util::errors::CargoResult;
-use crate::util::important_paths::find_root_manifest_for_wd;
-use crate::util::network;
-use crate::util::{truncate_with_ellipsis, IntoUrl};
-use crate::util::{Progress, ProgressStyle};
-use crate::{drop_print, drop_println, version};
-
-/// Registry settings loaded from config files.
-///
-/// This is loaded based on the `--registry` flag and the config settings.
-#[derive(Debug, PartialEq)]
-pub enum RegistryCredentialConfig {
- None,
- /// The authentication token.
- Token(Secret<String>),
- /// Process used for fetching a token.
- Process((PathBuf, Vec<String>)),
- /// Secret Key and subject for Asymmetric tokens.
- AsymmetricKey((Secret<String>, Option<String>)),
-}
-
-impl RegistryCredentialConfig {
- /// Returns `true` if the credential is [`None`].
- ///
- /// [`None`]: Self::None
- pub fn is_none(&self) -> bool {
- matches!(self, Self::None)
- }
- /// Returns `true` if the credential is [`Token`].
- ///
- /// [`Token`]: Self::Token
- pub fn is_token(&self) -> bool {
- matches!(self, Self::Token(..))
- }
- /// Returns `true` if the credential is [`AsymmetricKey`].
- ///
- /// [`AsymmetricKey`]: RegistryCredentialConfig::AsymmetricKey
- pub fn is_asymmetric_key(&self) -> bool {
- matches!(self, Self::AsymmetricKey(..))
- }
- pub fn as_token(&self) -> Option<Secret<&str>> {
- if let Self::Token(v) = self {
- Some(v.as_deref())
- } else {
- None
- }
- }
- pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> {
- if let Self::Process(v) = self {
- Some(v)
- } else {
- None
- }
- }
- pub fn as_asymmetric_key(&self) -> Option<&(Secret<String>, Option<String>)> {
- if let Self::AsymmetricKey(v) = self {
- Some(v)
- } else {
- None
- }
- }
-}
-
-pub struct PublishOpts<'cfg> {
- pub config: &'cfg Config,
- pub token: Option<Secret<String>>,
- pub index: Option<String>,
- pub verify: bool,
- pub allow_dirty: bool,
- pub jobs: Option<i32>,
- pub keep_going: bool,
- pub to_publish: ops::Packages,
- pub targets: Vec<String>,
- pub dry_run: bool,
- pub registry: Option<String>,
- pub cli_features: CliFeatures,
-}
-
-pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
- let specs = opts.to_publish.to_package_id_specs(ws)?;
- if specs.len() > 1 {
- bail!("the `-p` argument must be specified to select a single package to publish")
- }
- if Packages::Default == opts.to_publish && ws.is_virtual() {
- bail!("the `-p` argument must be specified in the root of a virtual workspace")
- }
- let member_ids = ws.members().map(|p| p.package_id());
- // Check that the spec matches exactly one member.
- specs[0].query(member_ids)?;
- let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?;
- // In `members_with_features_old`, it will add "current" package (determined by the cwd)
- // So we need filter
- pkgs = pkgs
- .into_iter()
- .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id())))
- .collect();
- // Double check. It is safe theoretically, unless logic has updated.
- assert_eq!(pkgs.len(), 1);
-
- let (pkg, cli_features) = pkgs.pop().unwrap();
-
- let mut publish_registry = opts.registry.clone();
- if let Some(ref allowed_registries) = *pkg.publish() {
- if publish_registry.is_none() && allowed_registries.len() == 1 {
- // If there is only one allowed registry, push to that one directly,
- // even though there is no registry specified in the command.
- let default_registry = &allowed_registries[0];
- if default_registry != CRATES_IO_REGISTRY {
- // Don't change the registry for crates.io and don't warn the user.
- // crates.io will be defaulted even without this.
- opts.config.shell().note(&format!(
- "Found `{}` as only allowed registry. Publishing to it automatically.",
- default_registry
- ))?;
- publish_registry = Some(default_registry.clone());
- }
- }
-
- let reg_name = publish_registry
- .clone()
- .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string());
- 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.",
- pkg.name(),
- );
- } else if !allowed_registries.contains(&reg_name) {
- bail!(
- "`{}` cannot be published.\n\
- The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
- pkg.name(),
- reg_name
- );
- }
- }
- // This is only used to confirm that we can create a token before we build the package.
- // This causes the credential provider to be called an extra time, but keeps the same order of errors.
- let ver = pkg.version().to_string();
- let mutation = auth::Mutation::PrePublish;
-
- let (mut registry, reg_ids) = registry(
- opts.config,
- opts.token.as_ref().map(Secret::as_deref),
- opts.index.as_deref(),
- publish_registry.as_deref(),
- true,
- Some(mutation).filter(|_| !opts.dry_run),
- )?;
- verify_dependencies(pkg, &registry, reg_ids.original)?;
-
- // Prepare a tarball, with a non-suppressible warning if metadata
- // is missing since this is being put online.
- let tarball = ops::package_one(
- ws,
- pkg,
- &ops::PackageOpts {
- config: opts.config,
- verify: opts.verify,
- list: false,
- check_metadata: true,
- allow_dirty: opts.allow_dirty,
- to_package: ops::Packages::Default,
- targets: opts.targets.clone(),
- jobs: opts.jobs,
- keep_going: opts.keep_going,
- cli_features: cli_features,
- },
- )?
- .unwrap();
-
- if !opts.dry_run {
- let hash = cargo_util::Sha256::new()
- .update_file(tarball.file())?
- .finish_hex();
- let mutation = Some(auth::Mutation::Publish {
- name: pkg.name().as_str(),
- vers: &ver,
- cksum: &hash,
- });
- registry.set_token(Some(auth::auth_token(
- &opts.config,
- &reg_ids.original,
- None,
- mutation,
- )?));
- }
-
- opts.config
- .shell()
- .status("Uploading", pkg.package_id().to_string())?;
- transmit(
- opts.config,
- pkg,
- tarball.file(),
- &mut registry,
- reg_ids.original,
- opts.dry_run,
- )?;
- if !opts.dry_run {
- const DEFAULT_TIMEOUT: u64 = 60;
- let timeout = if opts.config.cli_unstable().publish_timeout {
- let timeout: Option<u64> = opts.config.get("publish.timeout")?;
- timeout.unwrap_or(DEFAULT_TIMEOUT)
- } else {
- DEFAULT_TIMEOUT
- };
- if 0 < timeout {
- let timeout = std::time::Duration::from_secs(timeout);
- wait_for_publish(opts.config, reg_ids.original, pkg, timeout)?;
- }
- }
-
- Ok(())
-}
-
-fn verify_dependencies(
- pkg: &Package,
- registry: &Registry,
- registry_src: SourceId,
-) -> CargoResult<()> {
- for dep in pkg.dependencies().iter() {
- if super::check_dep_has_version(dep, true)? {
- continue;
- }
- // TomlManifest::prepare_for_publish will rewrite the dependency
- // to be just the `version` field.
- if dep.source_id() != registry_src {
- if !dep.source_id().is_registry() {
- // Consider making SourceId::kind a public type that we can
- // exhaustively match on. Using match can help ensure that
- // every kind is properly handled.
- panic!("unexpected source kind for dependency {:?}", dep);
- }
- // Block requests to send to crates.io with alt-registry deps.
- // This extra hostname check is mostly to assist with testing,
- // but also prevents someone using `--index` to specify
- // something that points to crates.io.
- if registry_src.is_crates_io() || registry.host_is_crates_io() {
- bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
- registries. `{}` needs to be published to crates.io before publishing this crate.\n\
- (crate `{}` is pulled from {})",
- dep.package_name(),
- dep.package_name(),
- dep.source_id());
- }
- }
- }
- Ok(())
-}
-
-fn transmit(
- config: &Config,
- pkg: &Package,
- tarball: &File,
- registry: &mut Registry,
- registry_id: SourceId,
- dry_run: bool,
-) -> CargoResult<()> {
- let deps = pkg
- .dependencies()
- .iter()
- .filter(|dep| {
- // Skip dev-dependency without version.
- dep.is_transitive() || dep.specified_req()
- })
- .map(|dep| {
- // If the dependency is from a different registry, then include the
- // registry in the dependency.
- let dep_registry_id = match dep.registry_id() {
- Some(id) => id,
- None => SourceId::crates_io(config)?,
- };
- // In the index and Web API, None means "from the same registry"
- // whereas in Cargo.toml, it means "from crates.io".
- let dep_registry = if dep_registry_id != registry_id {
- Some(dep_registry_id.url().to_string())
- } else {
- None
- };
-
- Ok(NewCrateDependency {
- optional: dep.is_optional(),
- default_features: dep.uses_default_features(),
- name: dep.package_name().to_string(),
- features: dep.features().iter().map(|s| s.to_string()).collect(),
- version_req: dep.version_req().to_string(),
- target: dep.platform().map(|s| s.to_string()),
- kind: match dep.kind() {
- DepKind::Normal => "normal",
- DepKind::Build => "build",
- DepKind::Development => "dev",
- }
- .to_string(),
- registry: dep_registry,
- explicit_name_in_toml: dep.explicit_name_in_toml().map(|s| s.to_string()),
- })
- })
- .collect::<CargoResult<Vec<NewCrateDependency>>>()?;
- let manifest = pkg.manifest();
- let ManifestMetadata {
- ref authors,
- ref description,
- ref homepage,
- ref documentation,
- ref keywords,
- ref readme,
- ref repository,
- ref license,
- ref license_file,
- ref categories,
- ref badges,
- ref links,
- ref rust_version,
- } = *manifest.metadata();
- let readme_content = readme
- .as_ref()
- .map(|readme| {
- paths::read(&pkg.root().join(readme))
- .with_context(|| format!("failed to read `readme` file for package `{}`", pkg))
- })
- .transpose()?;
- if let Some(ref file) = *license_file {
- if !pkg.root().join(file).exists() {
- bail!("the license file `{}` does not exist", file)
- }
- }
-
- // Do not upload if performing a dry run
- if dry_run {
- config.shell().warn("aborting upload due to dry run")?;
- return Ok(());
- }
-
- let string_features = match manifest.original().features() {
- Some(features) => features
- .iter()
- .map(|(feat, values)| {
- (
- feat.to_string(),
- values.iter().map(|fv| fv.to_string()).collect(),
- )
- })
- .collect::<BTreeMap<String, Vec<String>>>(),
- None => BTreeMap::new(),
- };
-
- let warnings = registry
- .publish(
- &NewCrate {
- name: pkg.name().to_string(),
- vers: pkg.version().to_string(),
- deps,
- features: string_features,
- authors: authors.clone(),
- description: description.clone(),
- homepage: homepage.clone(),
- documentation: documentation.clone(),
- keywords: keywords.clone(),
- categories: categories.clone(),
- readme: readme_content,
- readme_file: readme.clone(),
- repository: repository.clone(),
- license: license.clone(),
- license_file: license_file.clone(),
- badges: badges.clone(),
- links: links.clone(),
- rust_version: rust_version.clone(),
- },
- tarball,
- )
- .with_context(|| format!("failed to publish to registry at {}", registry.host()))?;
-
- if !warnings.invalid_categories.is_empty() {
- let msg = format!(
- "the following are not valid category slugs and were \
- ignored: {}. Please see https://crates.io/category_slugs \
- for the list of all category slugs. \
- ",
- warnings.invalid_categories.join(", ")
- );
- config.shell().warn(&msg)?;
- }
-
- if !warnings.invalid_badges.is_empty() {
- let msg = format!(
- "the following are not valid badges and were ignored: {}. \
- Either the badge type specified is unknown or a required \
- attribute is missing. Please see \
- https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata \
- for valid badge types and their required attributes.",
- warnings.invalid_badges.join(", ")
- );
- config.shell().warn(&msg)?;
- }
-
- if !warnings.other.is_empty() {
- for msg in warnings.other {
- config.shell().warn(&msg)?;
- }
- }
-
- Ok(())
-}
-
-fn wait_for_publish(
- config: &Config,
- registry_src: SourceId,
- pkg: &Package,
- timeout: std::time::Duration,
-) -> CargoResult<()> {
- let version_req = format!("={}", pkg.version());
- let mut source = SourceConfigMap::empty(config)?.load(registry_src, &HashSet::new())?;
- // Disable the source's built-in progress bars. Repeatedly showing a bunch
- // of independent progress bars can be a little confusing. There is an
- // overall progress bar managed here.
- source.set_quiet(true);
- let source_description = source.source_id().to_string();
- let query = Dependency::parse(pkg.name(), Some(&version_req), registry_src)?;
-
- let now = std::time::Instant::now();
- let sleep_time = std::time::Duration::from_secs(1);
- let max = timeout.as_secs() as usize;
- // Short does not include the registry name.
- let short_pkg_description = format!("{} v{}", pkg.name(), pkg.version());
- config.shell().status(
- "Uploaded",
- format!("{short_pkg_description} to {source_description}"),
- )?;
- config.shell().note(format!(
- "Waiting for `{short_pkg_description}` to be available at {source_description}.\n\
- You may press ctrl-c to skip waiting; the crate should be available shortly."
- ))?;
- let mut progress = Progress::with_style("Waiting", ProgressStyle::Ratio, config);
- progress.tick_now(0, max, "")?;
- let is_available = loop {
- {
- let _lock = config.acquire_package_cache_lock()?;
- // Force re-fetching the source
- //
- // As pulling from a git source is expensive, we track when we've done it within the
- // process to only do it once, but we are one of the rare cases that needs to do it
- // multiple times
- config
- .updated_sources()
- .remove(&source.replaced_source_id());
- source.invalidate_cache();
- let summaries = loop {
- // Exact to avoid returning all for path/git
- match source.query_vec(&query, QueryKind::Exact) {
- std::task::Poll::Ready(res) => {
- break res?;
- }
- std::task::Poll::Pending => source.block_until_ready()?,
- }
- };
- if !summaries.is_empty() {
- break true;
- }
- }
-
- let elapsed = now.elapsed();
- if timeout < elapsed {
- config.shell().warn(format!(
- "timed out waiting for `{short_pkg_description}` to be available in {source_description}",
- ))?;
- config.shell().note(
- "The registry may have a backlog that is delaying making the \
- crate available. The crate should be available soon.",
- )?;
- break false;
- }
-
- progress.tick_now(elapsed.as_secs() as usize, max, "")?;
- std::thread::sleep(sleep_time);
- };
- if is_available {
- config.shell().status(
- "Published",
- format!("{short_pkg_description} at {source_description}"),
- )?;
- }
-
- Ok(())
-}
-
-/// Returns the `Registry` and `Source` based on command-line and config settings.
-///
-/// * `token_from_cmdline`: The token from the command-line. If not set, uses the token
-/// from the config.
-/// * `index`: The index URL from the command-line.
-/// * `registry`: The registry name from the command-line. If neither
-/// `registry`, or `index` are set, then uses `crates-io`.
-/// * `force_update`: If `true`, forces the index to be updated.
-/// * `token_required`: If `true`, the token will be set.
-fn registry(
- config: &Config,
- token_from_cmdline: Option<Secret<&str>>,
- index: Option<&str>,
- registry: Option<&str>,
- force_update: bool,
- token_required: Option<auth::Mutation<'_>>,
-) -> CargoResult<(Registry, RegistrySourceIds)> {
- let source_ids = get_source_id(config, index, registry)?;
-
- if token_required.is_some() && index.is_some() && token_from_cmdline.is_none() {
- bail!("command-line argument --index requires --token to be specified");
- }
- if let Some(token) = token_from_cmdline {
- auth::cache_token(config, &source_ids.original, token);
- }
-
- let cfg = {
- let _lock = config.acquire_package_cache_lock()?;
- let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?;
- // Only update the index if `force_update` is set.
- if force_update {
- src.invalidate_cache()
- }
- let cfg = loop {
- match src.config()? {
- Poll::Pending => src
- .block_until_ready()
- .with_context(|| format!("failed to update {}", source_ids.replacement))?,
- Poll::Ready(cfg) => break cfg,
- }
- };
- cfg.expect("remote registries must have config")
- };
- let api_host = cfg
- .api
- .ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?;
- let token = if token_required.is_some() || cfg.auth_required {
- Some(auth::auth_token(
- config,
- &source_ids.original,
- None,
- token_required,
- )?)
- } else {
- None
- };
- let handle = http_handle(config)?;
- Ok((
- Registry::new_handle(api_host, token, handle, cfg.auth_required),
- source_ids,
- ))
-}
-
-/// Creates a new HTTP handle with appropriate global configuration for cargo.
-pub fn http_handle(config: &Config) -> CargoResult<Easy> {
- let (mut handle, timeout) = http_handle_and_timeout(config)?;
- timeout.configure(&mut handle)?;
- Ok(handle)
-}
-
-pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeout)> {
- if config.frozen() {
- bail!(
- "attempting to make an HTTP request, but --frozen was \
- specified"
- )
- }
- if config.offline() {
- bail!(
- "attempting to make an HTTP request, but --offline was \
- specified"
- )
- }
-
- // The timeout option for libcurl by default times out the entire transfer,
- // but we probably don't want this. Instead we only set timeouts for the
- // connect phase as well as a "low speed" timeout so if we don't receive
- // many bytes in a large-ish period of time then we time out.
- let mut handle = Easy::new();
- let timeout = configure_http_handle(config, &mut handle)?;
- Ok((handle, timeout))
-}
-
-// Only use a custom transport if any HTTP options are specified,
-// such as proxies or custom certificate authorities.
-//
-// The custom transport, however, is not as well battle-tested.
-pub fn needs_custom_http_transport(config: &Config) -> CargoResult<bool> {
- Ok(
- network::proxy::http_proxy_exists(config.http_config()?, config)
- || *config.http_config()? != Default::default()
- || config.get_env_os("HTTP_TIMEOUT").is_some(),
- )
-}
-
-/// Configure a libcurl http handle with the defaults options for Cargo
-pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<HttpTimeout> {
- let http = config.http_config()?;
- if let Some(proxy) = network::proxy::http_proxy(http) {
- handle.proxy(&proxy)?;
- }
- if let Some(cainfo) = &http.cainfo {
- let cainfo = cainfo.resolve_path(config);
- handle.cainfo(&cainfo)?;
- }
- if let Some(check) = http.check_revoke {
- handle.ssl_options(SslOpt::new().no_revoke(!check))?;
- }
-
- if let Some(user_agent) = &http.user_agent {
- handle.useragent(user_agent)?;
- } else {
- handle.useragent(&format!("cargo {}", version()))?;
- }
-
- fn to_ssl_version(s: &str) -> CargoResult<SslVersion> {
- let version = match s {
- "default" => SslVersion::Default,
- "tlsv1" => SslVersion::Tlsv1,
- "tlsv1.0" => SslVersion::Tlsv10,
- "tlsv1.1" => SslVersion::Tlsv11,
- "tlsv1.2" => SslVersion::Tlsv12,
- "tlsv1.3" => SslVersion::Tlsv13,
- _ => bail!(
- "Invalid ssl version `{s}`,\
- choose from 'default', 'tlsv1', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'."
- ),
- };
- Ok(version)
- }
-
- // Empty string accept encoding expands to the encodings supported by the current libcurl.
- handle.accept_encoding("")?;
- if let Some(ssl_version) = &http.ssl_version {
- match ssl_version {
- SslVersionConfig::Single(s) => {
- let version = to_ssl_version(s.as_str())?;
- handle.ssl_version(version)?;
- }
- SslVersionConfig::Range(SslVersionConfigRange { min, max }) => {
- let min_version = min
- .as_ref()
- .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?;
- let max_version = max
- .as_ref()
- .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?;
- handle.ssl_min_max_version(min_version, max_version)?;
- }
- }
- } else if cfg!(windows) {
- // This is a temporary workaround for some bugs with libcurl and
- // schannel and TLS 1.3.
- //
- // Our libcurl on Windows is usually built with schannel.
- // On Windows 11 (or Windows Server 2022), libcurl recently (late
- // 2022) gained support for TLS 1.3 with schannel, and it now defaults
- // to 1.3. Unfortunately there have been some bugs with this.
- // https://github.com/curl/curl/issues/9431 is the most recent. Once
- // that has been fixed, and some time has passed where we can be more
- // confident that the 1.3 support won't cause issues, this can be
- // removed.
- //
- // Windows 10 is unaffected. libcurl does not support TLS 1.3 on
- // Windows 10. (Windows 10 sorta had support, but it required enabling
- // an advanced option in the registry which was buggy, and libcurl
- // does runtime checks to prevent it.)
- handle.ssl_min_max_version(SslVersion::Default, SslVersion::Tlsv12)?;
- }
-
- if let Some(true) = http.debug {
- handle.verbose(true)?;
- log::debug!("{:#?}", curl::Version::get());
- handle.debug_function(|kind, data| {
- let (prefix, level) = match kind {
- InfoType::Text => ("*", Level::Debug),
- InfoType::HeaderIn => ("<", Level::Debug),
- InfoType::HeaderOut => (">", Level::Debug),
- InfoType::DataIn => ("{", Level::Trace),
- InfoType::DataOut => ("}", Level::Trace),
- InfoType::SslDataIn | InfoType::SslDataOut => return,
- _ => return,
- };
- let starts_with_ignore_case = |line: &str, text: &str| -> bool {
- line[..line.len().min(text.len())].eq_ignore_ascii_case(text)
- };
- match str::from_utf8(data) {
- Ok(s) => {
- for mut line in s.lines() {
- if starts_with_ignore_case(line, "authorization:") {
- line = "Authorization: [REDACTED]";
- } else if starts_with_ignore_case(line, "h2h3 [authorization:") {
- line = "h2h3 [Authorization: [REDACTED]]";
- } else if starts_with_ignore_case(line, "set-cookie") {
- line = "set-cookie: [REDACTED]";
- }
- log!(level, "http-debug: {} {}", prefix, line);
- }
- }
- Err(_) => {
- log!(
- level,
- "http-debug: {} ({} bytes of data)",
- prefix,
- data.len()
- );
- }
- }
- })?;
- }
-
- HttpTimeout::new(config)
-}
-
-#[must_use]
-pub struct HttpTimeout {
- pub dur: Duration,
- pub low_speed_limit: u32,
-}
-
-impl HttpTimeout {
- pub fn new(config: &Config) -> CargoResult<HttpTimeout> {
- let http_config = config.http_config()?;
- let low_speed_limit = http_config.low_speed_limit.unwrap_or(10);
- let seconds = http_config
- .timeout
- .or_else(|| {
- config
- .get_env("HTTP_TIMEOUT")
- .ok()
- .and_then(|s| s.parse().ok())
- })
- .unwrap_or(30);
- Ok(HttpTimeout {
- dur: Duration::new(seconds, 0),
- low_speed_limit,
- })
- }
-
- pub fn configure(&self, handle: &mut Easy) -> CargoResult<()> {
- // The timeout option for libcurl by default times out the entire
- // transfer, but we probably don't want this. Instead we only set
- // timeouts for the connect phase as well as a "low speed" timeout so
- // if we don't receive many bytes in a large-ish period of time then we
- // time out.
- handle.connect_timeout(self.dur)?;
- handle.low_speed_time(self.dur)?;
- handle.low_speed_limit(self.low_speed_limit)?;
- Ok(())
- }
-}
-
-pub fn registry_login(
- config: &Config,
- token: Option<Secret<&str>>,
- reg: Option<&str>,
- generate_keypair: bool,
- secret_key_required: bool,
- key_subject: Option<&str>,
-) -> CargoResult<()> {
- let source_ids = get_source_id(config, None, reg)?;
- let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
-
- let login_url = match registry(config, token.clone(), None, reg, false, None) {
- Ok((registry, _)) => Some(format!("{}/me", registry.host())),
- Err(e) if e.is::<AuthorizationError>() => e
- .downcast::<AuthorizationError>()
- .unwrap()
- .login_url
- .map(|u| u.to_string()),
- Err(e) => return Err(e),
- };
- let new_token;
- if generate_keypair || secret_key_required || key_subject.is_some() {
- if !config.cli_unstable().registry_auth {
- let flag = if generate_keypair {
- "generate-keypair"
- } else if secret_key_required {
- "secret-key"
- } else if key_subject.is_some() {
- "key-subject"
- } else {
- unreachable!("how did we get here");
- };
- bail!(
- "the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\
- See https://github.com/rust-lang/cargo/issues/10519 for more \
- information about the `{flag}` flag."
- );
- }
- assert!(token.is_none());
- // we are dealing with asymmetric tokens
- let (old_secret_key, old_key_subject) = match &reg_cfg {
- RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => {
- (Some(old_secret_key), old_key_subject.clone())
- }
- _ => (None, None),
- };
- let secret_key: Secret<String>;
- if generate_keypair {
- assert!(!secret_key_required);
- let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
- secret_key = Secret::default().map(|mut key| {
- FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
- key
- });
- } else if secret_key_required {
- assert!(!generate_keypair);
- drop_println!(config, "please paste the API secret key below");
- secret_key = Secret::default()
- .map(|mut line| {
- let input = io::stdin();
- input
- .lock()
- .read_line(&mut line)
- .with_context(|| "failed to read stdin")
- .map(|_| line.trim().to_string())
- })
- .transpose()?;
- } else {
- secret_key = old_secret_key
- .cloned()
- .ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?;
- }
- if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
- drop_println!(config, "{}", &p);
- } else {
- bail!("not a validly formatted PASERK secret key");
- }
- new_token = RegistryCredentialConfig::AsymmetricKey((
- secret_key,
- match key_subject {
- Some(key_subject) => Some(key_subject.to_string()),
- None => old_key_subject,
- },
- ));
- } else {
- new_token = RegistryCredentialConfig::Token(match token {
- Some(token) => token.owned(),
- None => {
- if let Some(login_url) = login_url {
- drop_println!(
- config,
- "please paste the token found on {} below",
- login_url
- )
- } else {
- drop_println!(
- config,
- "please paste the token for {} below",
- source_ids.original.display_registry_name()
- )
- }
-
- let mut line = String::new();
- let input = io::stdin();
- input
- .lock()
- .read_line(&mut line)
- .with_context(|| "failed to read stdin")?;
- // Automatically remove `cargo login` from an inputted token to
- // allow direct pastes from `registry.host()`/me.
- Secret::from(line.replace("cargo login", "").trim().to_string())
- }
- });
-
- if let Some(tok) = new_token.as_token() {
- crates_io::check_token(tok.as_ref().expose())?;
- }
- }
- if &reg_cfg == &new_token {
- config.shell().status("Login", "already logged in")?;
- return Ok(());
- }
-
- auth::login(config, &source_ids.original, new_token)?;
-
- config.shell().status(
- "Login",
- format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)),
- )?;
- Ok(())
-}
-
-pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> {
- let source_ids = get_source_id(config, None, reg)?;
- let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
- let reg_name = source_ids.original.display_registry_name();
- if reg_cfg.is_none() {
- config.shell().status(
- "Logout",
- format!("not currently logged in to `{}`", reg_name),
- )?;
- return Ok(());
- }
- auth::logout(config, &source_ids.original)?;
- config.shell().status(
- "Logout",
- format!(
- "token for `{}` has been removed from local storage",
- reg_name
- ),
- )?;
- let location = if source_ids.original.is_crates_io() {
- "<https://crates.io/me>".to_string()
- } else {
- // The URL for the source requires network access to load the config.
- // That could be a fairly heavy operation to perform just to provide a
- // help message, so for now this just provides some generic text.
- // Perhaps in the future this could have an API to fetch the config if
- // it is cached, but avoid network access otherwise?
- format!("the `{reg_name}` website")
- };
- config.shell().note(format!(
- "This does not revoke the token on the registry server.\n \
- If you need to revoke the token, visit {location} and follow the instructions there."
- ))?;
- Ok(())
-}
-
-pub struct OwnersOptions {
- pub krate: Option<String>,
- pub token: Option<Secret<String>>,
- pub index: Option<String>,
- pub to_add: Option<Vec<String>>,
- pub to_remove: Option<Vec<String>>,
- pub list: bool,
- pub registry: Option<String>,
-}
-
-pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
- let name = match opts.krate {
- Some(ref name) => name.clone(),
- None => {
- let manifest_path = find_root_manifest_for_wd(config.cwd())?;
- let ws = Workspace::new(&manifest_path, config)?;
- ws.current()?.package_id().name().to_string()
- }
- };
-
- let mutation = auth::Mutation::Owners { name: &name };
-
- let (mut registry, _) = registry(
- config,
- opts.token.as_ref().map(Secret::as_deref),
- opts.index.as_deref(),
- opts.registry.as_deref(),
- true,
- Some(mutation),
- )?;
-
- if let Some(ref v) = opts.to_add {
- let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
- let msg = registry.add_owners(&name, &v).with_context(|| {
- format!(
- "failed to invite owners to crate `{}` on registry at {}",
- name,
- registry.host()
- )
- })?;
-
- config.shell().status("Owner", msg)?;
- }
-
- if let Some(ref v) = opts.to_remove {
- let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
- config
- .shell()
- .status("Owner", format!("removing {:?} from crate {}", v, name))?;
- registry.remove_owners(&name, &v).with_context(|| {
- format!(
- "failed to remove owners from crate `{}` on registry at {}",
- name,
- registry.host()
- )
- })?;
- }
-
- if opts.list {
- let owners = registry.list_owners(&name).with_context(|| {
- format!(
- "failed to list owners of crate `{}` on registry at {}",
- name,
- registry.host()
- )
- })?;
- for owner in owners.iter() {
- drop_print!(config, "{}", owner.login);
- match (owner.name.as_ref(), owner.email.as_ref()) {
- (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email),
- (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s),
- (None, None) => drop_println!(config),
- }
- }
- }
-
- Ok(())
-}
-
-pub fn yank(
- config: &Config,
- krate: Option<String>,
- version: Option<String>,
- token: Option<Secret<String>>,
- index: Option<String>,
- undo: bool,
- reg: Option<String>,
-) -> CargoResult<()> {
- let name = match krate {
- Some(name) => name,
- None => {
- let manifest_path = find_root_manifest_for_wd(config.cwd())?;
- let ws = Workspace::new(&manifest_path, config)?;
- ws.current()?.package_id().name().to_string()
- }
- };
- let version = match version {
- Some(v) => v,
- None => bail!("a version must be specified to yank"),
- };
-
- let message = if undo {
- auth::Mutation::Unyank {
- name: &name,
- vers: &version,
- }
- } else {
- auth::Mutation::Yank {
- name: &name,
- vers: &version,
- }
- };
-
- let (mut registry, _) = registry(
- config,
- token.as_ref().map(Secret::as_deref),
- index.as_deref(),
- reg.as_deref(),
- true,
- Some(message),
- )?;
-
- let package_spec = format!("{}@{}", name, version);
- if undo {
- config.shell().status("Unyank", package_spec)?;
- registry.unyank(&name, &version).with_context(|| {
- format!(
- "failed to undo a yank from the registry at {}",
- registry.host()
- )
- })?;
- } else {
- config.shell().status("Yank", package_spec)?;
- registry
- .yank(&name, &version)
- .with_context(|| format!("failed to yank from the registry at {}", registry.host()))?;
- }
-
- Ok(())
-}
-
-/// Gets the SourceId for an index or registry setting.
-///
-/// The `index` and `reg` values are from the command-line or config settings.
-/// If both are None, and no source-replacement is configured, returns the source for crates.io.
-/// If both are None, and source replacement is configured, returns an error.
-///
-/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending
-/// on configuration.
-///
-/// If `reg` is set, source replacement is not followed.
-///
-/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of
-/// crates.io (such as index.crates.io), while the second is always the original source.
-fn get_source_id(
- config: &Config,
- index: Option<&str>,
- reg: Option<&str>,
-) -> CargoResult<RegistrySourceIds> {
- let sid = match (reg, index) {
- (None, None) => SourceId::crates_io(config)?,
- (_, Some(i)) => SourceId::for_registry(&i.into_url()?)?,
- (Some(r), None) => SourceId::alt_registry(config, r)?,
- };
- // Load source replacements that are built-in to Cargo.
- let builtin_replacement_sid = SourceConfigMap::empty(config)?
- .load(sid, &HashSet::new())?
- .replaced_source_id();
- let replacement_sid = SourceConfigMap::new(config)?
- .load(sid, &HashSet::new())?
- .replaced_source_id();
- if reg.is_none() && index.is_none() && replacement_sid != builtin_replacement_sid {
- // Neither --registry nor --index was passed and the user has configured source-replacement.
- if let Some(replacement_name) = replacement_sid.alt_registry_key() {
- bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`");
- } else {
- bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io");
- }
- } else {
- Ok(RegistrySourceIds {
- original: sid,
- replacement: builtin_replacement_sid,
- })
- }
-}
-
-struct RegistrySourceIds {
- /// Use when looking up the auth token, or writing out `Cargo.lock`
- original: SourceId,
- /// Use when interacting with the source (querying / publishing , etc)
- ///
- /// The source for crates.io may be replaced by a built-in source for accessing crates.io with
- /// the sparse protocol, or a source for the testing framework (when the replace_crates_io
- /// function is used)
- ///
- /// User-defined source replacement is not applied.
- replacement: SourceId,
-}
-
-pub fn search(
- query: &str,
- config: &Config,
- index: Option<String>,
- limit: u32,
- reg: Option<String>,
-) -> CargoResult<()> {
- let (mut registry, source_ids) =
- registry(config, None, index.as_deref(), reg.as_deref(), false, None)?;
- let (crates, total_crates) = registry.search(query, limit).with_context(|| {
- format!(
- "failed to retrieve search results from the registry at {}",
- registry.host()
- )
- })?;
-
- let names = crates
- .iter()
- .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_length = cmp::max(80, 128 - description_margin);
-
- let descriptions = crates.iter().map(|krate| {
- krate
- .description
- .as_ref()
- .map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length))
- });
-
- 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
- }
- None => name,
- };
- let mut fragments = line.split(query).peekable();
- while let Some(fragment) = fragments.next() {
- let _ = config.shell().write_stdout(fragment, &ColorSpec::new());
- if fragments.peek().is_some() {
- let _ = config
- .shell()
- .write_stdout(query, &ColorSpec::new().set_bold(true).set_fg(Some(Green)));
- }
- }
- let _ = config.shell().write_stdout("\n", &ColorSpec::new());
- }
-
- 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
- ),
- &ColorSpec::new(),
- );
- } else if total_crates > limit && limit >= search_max_limit {
- let extra = if source_ids.original.is_crates_io() {
- let url = Url::parse_with_params("https://crates.io/search", &[("q", query)])?;
- format!(" (go to {url} to see more)")
- } else {
- String::new()
- };
- let _ = config.shell().write_stdout(
- format_args!("... and {} crates more{}\n", total_crates - limit, extra),
- &ColorSpec::new(),
- );
- }
-
- Ok(())
-}
diff --git a/src/tools/cargo/src/cargo/ops/registry/login.rs b/src/tools/cargo/src/cargo/ops/registry/login.rs
new file mode 100644
index 000000000..1e2b3a87b
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/login.rs
@@ -0,0 +1,160 @@
+//! Interacts with the registry [login API][1].
+//!
+//! This doesn't really call any web API at this moment. Instead, it's just an
+//! operation for `cargo login`.
+//!
+//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#login
+
+use std::io;
+use std::io::BufRead;
+
+use anyhow::anyhow;
+use anyhow::bail;
+use anyhow::Context as _;
+use pasetors::keys::AsymmetricKeyPair;
+use pasetors::keys::Generate as _;
+use pasetors::paserk::FormatAsPaserk;
+
+use crate::drop_println;
+use crate::ops::RegistryCredentialConfig;
+use crate::sources::CRATES_IO_DOMAIN;
+use crate::util::auth;
+use crate::util::auth::paserk_public_from_paserk_secret;
+use crate::util::auth::AuthorizationError;
+use crate::util::auth::Secret;
+use crate::CargoResult;
+use crate::Config;
+
+use super::get_source_id;
+
+pub fn registry_login(
+ config: &Config,
+ token: Option<Secret<&str>>,
+ reg: Option<&str>,
+ generate_keypair: bool,
+ secret_key_required: bool,
+ key_subject: Option<&str>,
+) -> CargoResult<()> {
+ let source_ids = get_source_id(config, None, reg)?;
+ let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
+
+ let login_url = match super::registry(config, token.clone(), None, reg, false, None) {
+ Ok((registry, _)) => Some(format!("{}/me", registry.host())),
+ Err(e) if e.is::<AuthorizationError>() => e
+ .downcast::<AuthorizationError>()
+ .unwrap()
+ .login_url
+ .map(|u| u.to_string()),
+ Err(e) => return Err(e),
+ };
+ let new_token;
+ if generate_keypair || secret_key_required || key_subject.is_some() {
+ if !config.cli_unstable().registry_auth {
+ let flag = if generate_keypair {
+ "generate-keypair"
+ } else if secret_key_required {
+ "secret-key"
+ } else if key_subject.is_some() {
+ "key-subject"
+ } else {
+ unreachable!("how did we get here");
+ };
+ bail!(
+ "the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\
+ See https://github.com/rust-lang/cargo/issues/10519 for more \
+ information about the `{flag}` flag."
+ );
+ }
+ assert!(token.is_none());
+ // we are dealing with asymmetric tokens
+ let (old_secret_key, old_key_subject) = match &reg_cfg {
+ RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => {
+ (Some(old_secret_key), old_key_subject.clone())
+ }
+ _ => (None, None),
+ };
+ let secret_key: Secret<String>;
+ if generate_keypair {
+ assert!(!secret_key_required);
+ let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
+ secret_key = Secret::default().map(|mut key| {
+ FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
+ key
+ });
+ } else if secret_key_required {
+ assert!(!generate_keypair);
+ drop_println!(config, "please paste the API secret key below");
+ secret_key = Secret::default()
+ .map(|mut line| {
+ let input = io::stdin();
+ input
+ .lock()
+ .read_line(&mut line)
+ .with_context(|| "failed to read stdin")
+ .map(|_| line.trim().to_string())
+ })
+ .transpose()?;
+ } else {
+ secret_key = old_secret_key
+ .cloned()
+ .ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?;
+ }
+ if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
+ drop_println!(config, "{}", &p);
+ } else {
+ bail!("not a validly formatted PASERK secret key");
+ }
+ new_token = RegistryCredentialConfig::AsymmetricKey((
+ secret_key,
+ match key_subject {
+ Some(key_subject) => Some(key_subject.to_string()),
+ None => old_key_subject,
+ },
+ ));
+ } else {
+ new_token = RegistryCredentialConfig::Token(match token {
+ Some(token) => token.owned(),
+ None => {
+ if let Some(login_url) = login_url {
+ drop_println!(
+ config,
+ "please paste the token found on {} below",
+ login_url
+ )
+ } else {
+ drop_println!(
+ config,
+ "please paste the token for {} below",
+ source_ids.original.display_registry_name()
+ )
+ }
+
+ let mut line = String::new();
+ let input = io::stdin();
+ input
+ .lock()
+ .read_line(&mut line)
+ .with_context(|| "failed to read stdin")?;
+ // Automatically remove `cargo login` from an inputted token to
+ // allow direct pastes from `registry.host()`/me.
+ Secret::from(line.replace("cargo login", "").trim().to_string())
+ }
+ });
+
+ if let Some(tok) = new_token.as_token() {
+ crates_io::check_token(tok.as_ref().expose())?;
+ }
+ }
+ if &reg_cfg == &new_token {
+ config.shell().status("Login", "already logged in")?;
+ return Ok(());
+ }
+
+ auth::login(config, &source_ids.original, new_token)?;
+
+ config.shell().status(
+ "Login",
+ format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)),
+ )?;
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/logout.rs b/src/tools/cargo/src/cargo/ops/registry/logout.rs
new file mode 100644
index 000000000..59f2d9261
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/logout.rs
@@ -0,0 +1,42 @@
+//! Interacts with the registry logout.
+//!
+//! There is no web API for logout at this moment. Instead, it's just an
+//! operation for `cargo logout`.
+
+use crate::util::auth;
+use crate::CargoResult;
+use crate::Config;
+
+use super::get_source_id;
+
+pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> {
+ let source_ids = get_source_id(config, None, reg)?;
+ let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
+ let reg_name = source_ids.original.display_registry_name();
+ if reg_cfg.is_none() {
+ config
+ .shell()
+ .status("Logout", format!("not currently logged in to `{reg_name}`"))?;
+ return Ok(());
+ }
+ auth::logout(config, &source_ids.original)?;
+ config.shell().status(
+ "Logout",
+ format!("token for `{reg_name}` has been removed from local storage"),
+ )?;
+ let location = if source_ids.original.is_crates_io() {
+ "<https://crates.io/me>".to_string()
+ } else {
+ // The URL for the source requires network access to load the config.
+ // That could be a fairly heavy operation to perform just to provide a
+ // help message, so for now this just provides some generic text.
+ // Perhaps in the future this could have an API to fetch the config if
+ // it is cached, but avoid network access otherwise?
+ format!("the `{reg_name}` website")
+ };
+ config.shell().note(format!(
+ "This does not revoke the token on the registry server.\n \
+ If you need to revoke the token, visit {location} and follow the instructions there."
+ ))?;
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/mod.rs b/src/tools/cargo/src/cargo/ops/registry/mod.rs
new file mode 100644
index 000000000..ecb610ddd
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/mod.rs
@@ -0,0 +1,213 @@
+//! Operations that interact with the [registry web API][1].
+//!
+//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html
+
+mod login;
+mod logout;
+mod owner;
+mod publish;
+mod search;
+mod yank;
+
+use std::collections::HashSet;
+use std::path::PathBuf;
+use std::str;
+use std::task::Poll;
+
+use anyhow::{bail, format_err, Context as _};
+use crates_io::{self, Registry};
+
+use crate::core::source::Source;
+use crate::core::SourceId;
+use crate::sources::{RegistrySource, SourceConfigMap};
+use crate::util::auth::{self, Secret};
+use crate::util::config::Config;
+use crate::util::errors::CargoResult;
+use crate::util::network::http::http_handle;
+use crate::util::IntoUrl;
+
+pub use self::login::registry_login;
+pub use self::logout::registry_logout;
+pub use self::owner::modify_owners;
+pub use self::owner::OwnersOptions;
+pub use self::publish::publish;
+pub use self::publish::PublishOpts;
+pub use self::search::search;
+pub use self::yank::yank;
+
+/// Registry settings loaded from config files.
+///
+/// This is loaded based on the `--registry` flag and the config settings.
+#[derive(Debug, PartialEq)]
+pub enum RegistryCredentialConfig {
+ None,
+ /// The authentication token.
+ Token(Secret<String>),
+ /// Process used for fetching a token.
+ Process((PathBuf, Vec<String>)),
+ /// Secret Key and subject for Asymmetric tokens.
+ AsymmetricKey((Secret<String>, Option<String>)),
+}
+
+impl RegistryCredentialConfig {
+ /// Returns `true` if the credential is [`None`].
+ ///
+ /// [`None`]: Self::None
+ pub fn is_none(&self) -> bool {
+ matches!(self, Self::None)
+ }
+ /// Returns `true` if the credential is [`Token`].
+ ///
+ /// [`Token`]: Self::Token
+ pub fn is_token(&self) -> bool {
+ matches!(self, Self::Token(..))
+ }
+ /// Returns `true` if the credential is [`AsymmetricKey`].
+ ///
+ /// [`AsymmetricKey`]: RegistryCredentialConfig::AsymmetricKey
+ pub fn is_asymmetric_key(&self) -> bool {
+ matches!(self, Self::AsymmetricKey(..))
+ }
+ pub fn as_token(&self) -> Option<Secret<&str>> {
+ if let Self::Token(v) = self {
+ Some(v.as_deref())
+ } else {
+ None
+ }
+ }
+ pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> {
+ if let Self::Process(v) = self {
+ Some(v)
+ } else {
+ None
+ }
+ }
+ pub fn as_asymmetric_key(&self) -> Option<&(Secret<String>, Option<String>)> {
+ if let Self::AsymmetricKey(v) = self {
+ Some(v)
+ } else {
+ None
+ }
+ }
+}
+
+/// Returns the `Registry` and `Source` based on command-line and config settings.
+///
+/// * `token_from_cmdline`: The token from the command-line. If not set, uses the token
+/// from the config.
+/// * `index`: The index URL from the command-line.
+/// * `registry`: The registry name from the command-line. If neither
+/// `registry`, or `index` are set, then uses `crates-io`.
+/// * `force_update`: If `true`, forces the index to be updated.
+/// * `token_required`: If `true`, the token will be set.
+fn registry(
+ config: &Config,
+ token_from_cmdline: Option<Secret<&str>>,
+ index: Option<&str>,
+ registry: Option<&str>,
+ force_update: bool,
+ token_required: Option<auth::Mutation<'_>>,
+) -> CargoResult<(Registry, RegistrySourceIds)> {
+ let source_ids = get_source_id(config, index, registry)?;
+
+ if token_required.is_some() && index.is_some() && token_from_cmdline.is_none() {
+ bail!("command-line argument --index requires --token to be specified");
+ }
+ if let Some(token) = token_from_cmdline {
+ auth::cache_token(config, &source_ids.original, token);
+ }
+
+ let cfg = {
+ let _lock = config.acquire_package_cache_lock()?;
+ let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?;
+ // Only update the index if `force_update` is set.
+ if force_update {
+ src.invalidate_cache()
+ }
+ let cfg = loop {
+ match src.config()? {
+ Poll::Pending => src
+ .block_until_ready()
+ .with_context(|| format!("failed to update {}", source_ids.replacement))?,
+ Poll::Ready(cfg) => break cfg,
+ }
+ };
+ cfg.expect("remote registries must have config")
+ };
+ let api_host = cfg
+ .api
+ .ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?;
+ let token = if token_required.is_some() || cfg.auth_required {
+ Some(auth::auth_token(
+ config,
+ &source_ids.original,
+ None,
+ token_required,
+ )?)
+ } else {
+ None
+ };
+ let handle = http_handle(config)?;
+ Ok((
+ Registry::new_handle(api_host, token, handle, cfg.auth_required),
+ source_ids,
+ ))
+}
+
+/// Gets the SourceId for an index or registry setting.
+///
+/// The `index` and `reg` values are from the command-line or config settings.
+/// If both are None, and no source-replacement is configured, returns the source for crates.io.
+/// If both are None, and source replacement is configured, returns an error.
+///
+/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending
+/// on configuration.
+///
+/// If `reg` is set, source replacement is not followed.
+///
+/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of
+/// crates.io (such as index.crates.io), while the second is always the original source.
+fn get_source_id(
+ config: &Config,
+ index: Option<&str>,
+ reg: Option<&str>,
+) -> CargoResult<RegistrySourceIds> {
+ let sid = match (reg, index) {
+ (None, None) => SourceId::crates_io(config)?,
+ (_, Some(i)) => SourceId::for_registry(&i.into_url()?)?,
+ (Some(r), None) => SourceId::alt_registry(config, r)?,
+ };
+ // Load source replacements that are built-in to Cargo.
+ let builtin_replacement_sid = SourceConfigMap::empty(config)?
+ .load(sid, &HashSet::new())?
+ .replaced_source_id();
+ let replacement_sid = SourceConfigMap::new(config)?
+ .load(sid, &HashSet::new())?
+ .replaced_source_id();
+ if reg.is_none() && index.is_none() && replacement_sid != builtin_replacement_sid {
+ // Neither --registry nor --index was passed and the user has configured source-replacement.
+ if let Some(replacement_name) = replacement_sid.alt_registry_key() {
+ bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`");
+ } else {
+ bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io");
+ }
+ } else {
+ Ok(RegistrySourceIds {
+ original: sid,
+ replacement: builtin_replacement_sid,
+ })
+ }
+}
+
+struct RegistrySourceIds {
+ /// Use when looking up the auth token, or writing out `Cargo.lock`
+ original: SourceId,
+ /// Use when interacting with the source (querying / publishing , etc)
+ ///
+ /// The source for crates.io may be replaced by a built-in source for accessing crates.io with
+ /// the sparse protocol, or a source for the testing framework (when the replace_crates_io
+ /// function is used)
+ ///
+ /// User-defined source replacement is not applied.
+ replacement: SourceId,
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/owner.rs b/src/tools/cargo/src/cargo/ops/registry/owner.rs
new file mode 100644
index 000000000..e53e07cb8
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/owner.rs
@@ -0,0 +1,93 @@
+//! Interacts with the registry [owners API][1].
+//!
+//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#owners
+
+use anyhow::Context as _;
+
+use crate::core::Workspace;
+use crate::drop_print;
+use crate::drop_println;
+use crate::util::auth;
+use crate::util::auth::Secret;
+use crate::util::important_paths::find_root_manifest_for_wd;
+use crate::CargoResult;
+use crate::Config;
+
+pub struct OwnersOptions {
+ pub krate: Option<String>,
+ pub token: Option<Secret<String>>,
+ pub index: Option<String>,
+ pub to_add: Option<Vec<String>>,
+ pub to_remove: Option<Vec<String>>,
+ pub list: bool,
+ pub registry: Option<String>,
+}
+
+pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
+ let name = match opts.krate {
+ Some(ref name) => name.clone(),
+ None => {
+ let manifest_path = find_root_manifest_for_wd(config.cwd())?;
+ let ws = Workspace::new(&manifest_path, config)?;
+ ws.current()?.package_id().name().to_string()
+ }
+ };
+
+ let mutation = auth::Mutation::Owners { name: &name };
+
+ let (mut registry, _) = super::registry(
+ config,
+ opts.token.as_ref().map(Secret::as_deref),
+ opts.index.as_deref(),
+ opts.registry.as_deref(),
+ true,
+ Some(mutation),
+ )?;
+
+ if let Some(ref v) = opts.to_add {
+ let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
+ let msg = registry.add_owners(&name, &v).with_context(|| {
+ format!(
+ "failed to invite owners to crate `{}` on registry at {}",
+ name,
+ registry.host()
+ )
+ })?;
+
+ config.shell().status("Owner", msg)?;
+ }
+
+ if let Some(ref v) = opts.to_remove {
+ let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
+ config
+ .shell()
+ .status("Owner", format!("removing {:?} from crate {}", v, name))?;
+ registry.remove_owners(&name, &v).with_context(|| {
+ format!(
+ "failed to remove owners from crate `{}` on registry at {}",
+ name,
+ registry.host()
+ )
+ })?;
+ }
+
+ if opts.list {
+ let owners = registry.list_owners(&name).with_context(|| {
+ format!(
+ "failed to list owners of crate `{}` on registry at {}",
+ name,
+ registry.host()
+ )
+ })?;
+ for owner in owners.iter() {
+ drop_print!(config, "{}", owner.login);
+ match (owner.name.as_ref(), owner.email.as_ref()) {
+ (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email),
+ (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s),
+ (None, None) => drop_println!(config),
+ }
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs
new file mode 100644
index 000000000..7f4fbbae2
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs
@@ -0,0 +1,461 @@
+//! Interacts with the registry [publish API][1].
+//!
+//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#publish
+
+use std::collections::BTreeMap;
+use std::collections::HashSet;
+use std::fs::File;
+use std::time::Duration;
+
+use anyhow::bail;
+use anyhow::Context as _;
+use cargo_util::paths;
+use crates_io::NewCrate;
+use crates_io::NewCrateDependency;
+use crates_io::Registry;
+
+use crate::core::dependency::DepKind;
+use crate::core::manifest::ManifestMetadata;
+use crate::core::resolver::CliFeatures;
+use crate::core::Dependency;
+use crate::core::Package;
+use crate::core::QueryKind;
+use crate::core::SourceId;
+use crate::core::Workspace;
+use crate::ops;
+use crate::ops::PackageOpts;
+use crate::ops::Packages;
+use crate::sources::SourceConfigMap;
+use crate::sources::CRATES_IO_REGISTRY;
+use crate::util::auth;
+use crate::util::auth::Secret;
+use crate::util::config::JobsConfig;
+use crate::util::Progress;
+use crate::util::ProgressStyle;
+use crate::CargoResult;
+use crate::Config;
+
+use super::super::check_dep_has_version;
+
+pub struct PublishOpts<'cfg> {
+ pub config: &'cfg Config,
+ pub token: Option<Secret<String>>,
+ pub index: Option<String>,
+ pub verify: bool,
+ pub allow_dirty: bool,
+ pub jobs: Option<JobsConfig>,
+ pub keep_going: bool,
+ pub to_publish: ops::Packages,
+ pub targets: Vec<String>,
+ pub dry_run: bool,
+ pub registry: Option<String>,
+ pub cli_features: CliFeatures,
+}
+
+pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
+ let specs = opts.to_publish.to_package_id_specs(ws)?;
+ if specs.len() > 1 {
+ bail!("the `-p` argument must be specified to select a single package to publish")
+ }
+ if Packages::Default == opts.to_publish && ws.is_virtual() {
+ bail!("the `-p` argument must be specified in the root of a virtual workspace")
+ }
+ let member_ids = ws.members().map(|p| p.package_id());
+ // Check that the spec matches exactly one member.
+ specs[0].query(member_ids)?;
+ let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?;
+ // In `members_with_features_old`, it will add "current" package (determined by the cwd)
+ // So we need filter
+ pkgs = pkgs
+ .into_iter()
+ .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id())))
+ .collect();
+ // Double check. It is safe theoretically, unless logic has updated.
+ assert_eq!(pkgs.len(), 1);
+
+ let (pkg, cli_features) = pkgs.pop().unwrap();
+
+ let mut publish_registry = opts.registry.clone();
+ if let Some(ref allowed_registries) = *pkg.publish() {
+ if publish_registry.is_none() && allowed_registries.len() == 1 {
+ // If there is only one allowed registry, push to that one directly,
+ // even though there is no registry specified in the command.
+ let default_registry = &allowed_registries[0];
+ if default_registry != CRATES_IO_REGISTRY {
+ // Don't change the registry for crates.io and don't warn the user.
+ // crates.io will be defaulted even without this.
+ opts.config.shell().note(&format!(
+ "Found `{}` as only allowed registry. Publishing to it automatically.",
+ default_registry
+ ))?;
+ publish_registry = Some(default_registry.clone());
+ }
+ }
+
+ let reg_name = publish_registry
+ .clone()
+ .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string());
+ 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.",
+ pkg.name(),
+ );
+ } else if !allowed_registries.contains(&reg_name) {
+ bail!(
+ "`{}` cannot be published.\n\
+ The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
+ pkg.name(),
+ reg_name
+ );
+ }
+ }
+ // This is only used to confirm that we can create a token before we build the package.
+ // This causes the credential provider to be called an extra time, but keeps the same order of errors.
+ let ver = pkg.version().to_string();
+ let mutation = auth::Mutation::PrePublish;
+
+ let (mut registry, reg_ids) = super::registry(
+ opts.config,
+ opts.token.as_ref().map(Secret::as_deref),
+ opts.index.as_deref(),
+ publish_registry.as_deref(),
+ true,
+ Some(mutation).filter(|_| !opts.dry_run),
+ )?;
+ verify_dependencies(pkg, &registry, reg_ids.original)?;
+
+ // Prepare a tarball, with a non-suppressible warning if metadata
+ // is missing since this is being put online.
+ let tarball = ops::package_one(
+ ws,
+ pkg,
+ &PackageOpts {
+ config: opts.config,
+ verify: opts.verify,
+ list: false,
+ check_metadata: true,
+ allow_dirty: opts.allow_dirty,
+ to_package: Packages::Default,
+ targets: opts.targets.clone(),
+ jobs: opts.jobs.clone(),
+ keep_going: opts.keep_going,
+ cli_features,
+ },
+ )?
+ .unwrap();
+
+ if !opts.dry_run {
+ let hash = cargo_util::Sha256::new()
+ .update_file(tarball.file())?
+ .finish_hex();
+ let mutation = Some(auth::Mutation::Publish {
+ name: pkg.name().as_str(),
+ vers: &ver,
+ cksum: &hash,
+ });
+ registry.set_token(Some(auth::auth_token(
+ &opts.config,
+ &reg_ids.original,
+ None,
+ mutation,
+ )?));
+ }
+
+ opts.config
+ .shell()
+ .status("Uploading", pkg.package_id().to_string())?;
+ transmit(
+ opts.config,
+ pkg,
+ tarball.file(),
+ &mut registry,
+ reg_ids.original,
+ opts.dry_run,
+ )?;
+ if !opts.dry_run {
+ const DEFAULT_TIMEOUT: u64 = 60;
+ let timeout = if opts.config.cli_unstable().publish_timeout {
+ let timeout: Option<u64> = opts.config.get("publish.timeout")?;
+ timeout.unwrap_or(DEFAULT_TIMEOUT)
+ } else {
+ DEFAULT_TIMEOUT
+ };
+ if 0 < timeout {
+ let timeout = Duration::from_secs(timeout);
+ wait_for_publish(opts.config, reg_ids.original, pkg, timeout)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn wait_for_publish(
+ config: &Config,
+ registry_src: SourceId,
+ pkg: &Package,
+ timeout: Duration,
+) -> CargoResult<()> {
+ let version_req = format!("={}", pkg.version());
+ let mut source = SourceConfigMap::empty(config)?.load(registry_src, &HashSet::new())?;
+ // Disable the source's built-in progress bars. Repeatedly showing a bunch
+ // of independent progress bars can be a little confusing. There is an
+ // overall progress bar managed here.
+ source.set_quiet(true);
+ let source_description = source.source_id().to_string();
+ let query = Dependency::parse(pkg.name(), Some(&version_req), registry_src)?;
+
+ let now = std::time::Instant::now();
+ let sleep_time = Duration::from_secs(1);
+ let max = timeout.as_secs() as usize;
+ // Short does not include the registry name.
+ let short_pkg_description = format!("{} v{}", pkg.name(), pkg.version());
+ config.shell().status(
+ "Uploaded",
+ format!("{short_pkg_description} to {source_description}"),
+ )?;
+ config.shell().note(format!(
+ "Waiting for `{short_pkg_description}` to be available at {source_description}.\n\
+ You may press ctrl-c to skip waiting; the crate should be available shortly."
+ ))?;
+ let mut progress = Progress::with_style("Waiting", ProgressStyle::Ratio, config);
+ progress.tick_now(0, max, "")?;
+ let is_available = loop {
+ {
+ let _lock = config.acquire_package_cache_lock()?;
+ // Force re-fetching the source
+ //
+ // As pulling from a git source is expensive, we track when we've done it within the
+ // process to only do it once, but we are one of the rare cases that needs to do it
+ // multiple times
+ config
+ .updated_sources()
+ .remove(&source.replaced_source_id());
+ source.invalidate_cache();
+ let summaries = loop {
+ // Exact to avoid returning all for path/git
+ match source.query_vec(&query, QueryKind::Exact) {
+ std::task::Poll::Ready(res) => {
+ break res?;
+ }
+ std::task::Poll::Pending => source.block_until_ready()?,
+ }
+ };
+ if !summaries.is_empty() {
+ break true;
+ }
+ }
+
+ let elapsed = now.elapsed();
+ if timeout < elapsed {
+ config.shell().warn(format!(
+ "timed out waiting for `{short_pkg_description}` to be available in {source_description}",
+ ))?;
+ config.shell().note(
+ "The registry may have a backlog that is delaying making the \
+ crate available. The crate should be available soon.",
+ )?;
+ break false;
+ }
+
+ progress.tick_now(elapsed.as_secs() as usize, max, "")?;
+ std::thread::sleep(sleep_time);
+ };
+ if is_available {
+ config.shell().status(
+ "Published",
+ format!("{short_pkg_description} at {source_description}"),
+ )?;
+ }
+
+ Ok(())
+}
+
+fn verify_dependencies(
+ pkg: &Package,
+ registry: &Registry,
+ registry_src: SourceId,
+) -> CargoResult<()> {
+ for dep in pkg.dependencies().iter() {
+ if check_dep_has_version(dep, true)? {
+ continue;
+ }
+ // TomlManifest::prepare_for_publish will rewrite the dependency
+ // to be just the `version` field.
+ if dep.source_id() != registry_src {
+ if !dep.source_id().is_registry() {
+ // Consider making SourceId::kind a public type that we can
+ // exhaustively match on. Using match can help ensure that
+ // every kind is properly handled.
+ panic!("unexpected source kind for dependency {:?}", dep);
+ }
+ // Block requests to send to crates.io with alt-registry deps.
+ // This extra hostname check is mostly to assist with testing,
+ // but also prevents someone using `--index` to specify
+ // something that points to crates.io.
+ if registry_src.is_crates_io() || registry.host_is_crates_io() {
+ bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
+ registries. `{}` needs to be published to crates.io before publishing this crate.\n\
+ (crate `{}` is pulled from {})",
+ dep.package_name(),
+ dep.package_name(),
+ dep.source_id());
+ }
+ }
+ }
+ Ok(())
+}
+
+fn transmit(
+ config: &Config,
+ pkg: &Package,
+ tarball: &File,
+ registry: &mut Registry,
+ registry_id: SourceId,
+ dry_run: bool,
+) -> CargoResult<()> {
+ let deps = pkg
+ .dependencies()
+ .iter()
+ .filter(|dep| {
+ // Skip dev-dependency without version.
+ dep.is_transitive() || dep.specified_req()
+ })
+ .map(|dep| {
+ // If the dependency is from a different registry, then include the
+ // registry in the dependency.
+ let dep_registry_id = match dep.registry_id() {
+ Some(id) => id,
+ None => SourceId::crates_io(config)?,
+ };
+ // In the index and Web API, None means "from the same registry"
+ // whereas in Cargo.toml, it means "from crates.io".
+ let dep_registry = if dep_registry_id != registry_id {
+ Some(dep_registry_id.url().to_string())
+ } else {
+ None
+ };
+
+ Ok(NewCrateDependency {
+ optional: dep.is_optional(),
+ default_features: dep.uses_default_features(),
+ name: dep.package_name().to_string(),
+ features: dep.features().iter().map(|s| s.to_string()).collect(),
+ version_req: dep.version_req().to_string(),
+ target: dep.platform().map(|s| s.to_string()),
+ kind: match dep.kind() {
+ DepKind::Normal => "normal",
+ DepKind::Build => "build",
+ DepKind::Development => "dev",
+ }
+ .to_string(),
+ registry: dep_registry,
+ explicit_name_in_toml: dep.explicit_name_in_toml().map(|s| s.to_string()),
+ })
+ })
+ .collect::<CargoResult<Vec<NewCrateDependency>>>()?;
+ let manifest = pkg.manifest();
+ let ManifestMetadata {
+ ref authors,
+ ref description,
+ ref homepage,
+ ref documentation,
+ ref keywords,
+ ref readme,
+ ref repository,
+ ref license,
+ ref license_file,
+ ref categories,
+ ref badges,
+ ref links,
+ ref rust_version,
+ } = *manifest.metadata();
+ let readme_content = readme
+ .as_ref()
+ .map(|readme| {
+ paths::read(&pkg.root().join(readme))
+ .with_context(|| format!("failed to read `readme` file for package `{}`", pkg))
+ })
+ .transpose()?;
+ if let Some(ref file) = *license_file {
+ if !pkg.root().join(file).exists() {
+ bail!("the license file `{}` does not exist", file)
+ }
+ }
+
+ // Do not upload if performing a dry run
+ if dry_run {
+ config.shell().warn("aborting upload due to dry run")?;
+ return Ok(());
+ }
+
+ let string_features = match manifest.original().features() {
+ Some(features) => features
+ .iter()
+ .map(|(feat, values)| {
+ (
+ feat.to_string(),
+ values.iter().map(|fv| fv.to_string()).collect(),
+ )
+ })
+ .collect::<BTreeMap<String, Vec<String>>>(),
+ None => BTreeMap::new(),
+ };
+
+ let warnings = registry
+ .publish(
+ &NewCrate {
+ name: pkg.name().to_string(),
+ vers: pkg.version().to_string(),
+ deps,
+ features: string_features,
+ authors: authors.clone(),
+ description: description.clone(),
+ homepage: homepage.clone(),
+ documentation: documentation.clone(),
+ keywords: keywords.clone(),
+ categories: categories.clone(),
+ readme: readme_content,
+ readme_file: readme.clone(),
+ repository: repository.clone(),
+ license: license.clone(),
+ license_file: license_file.clone(),
+ badges: badges.clone(),
+ links: links.clone(),
+ rust_version: rust_version.clone(),
+ },
+ tarball,
+ )
+ .with_context(|| format!("failed to publish to registry at {}", registry.host()))?;
+
+ if !warnings.invalid_categories.is_empty() {
+ let msg = format!(
+ "the following are not valid category slugs and were \
+ ignored: {}. Please see https://crates.io/category_slugs \
+ for the list of all category slugs. \
+ ",
+ warnings.invalid_categories.join(", ")
+ );
+ config.shell().warn(&msg)?;
+ }
+
+ if !warnings.invalid_badges.is_empty() {
+ let msg = format!(
+ "the following are not valid badges and were ignored: {}. \
+ Either the badge type specified is unknown or a required \
+ attribute is missing. Please see \
+ https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata \
+ for valid badge types and their required attributes.",
+ warnings.invalid_badges.join(", ")
+ );
+ config.shell().warn(&msg)?;
+ }
+
+ if !warnings.other.is_empty() {
+ for msg in warnings.other {
+ config.shell().warn(&msg)?;
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/search.rs b/src/tools/cargo/src/cargo/ops/registry/search.rs
new file mode 100644
index 000000000..5e01ea98f
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/search.rs
@@ -0,0 +1,95 @@
+//! Interacts with the registry [search API][1].
+//!
+//! [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 termcolor::Color;
+use termcolor::ColorSpec;
+use url::Url;
+
+use crate::util::truncate_with_ellipsis;
+use crate::CargoResult;
+use crate::Config;
+
+pub fn search(
+ query: &str,
+ config: &Config,
+ index: Option<String>,
+ limit: u32,
+ reg: Option<String>,
+) -> CargoResult<()> {
+ let (mut registry, source_ids) =
+ super::registry(config, None, index.as_deref(), reg.as_deref(), false, None)?;
+ let (crates, total_crates) = registry.search(query, limit).with_context(|| {
+ format!(
+ "failed to retrieve search results from the registry at {}",
+ registry.host()
+ )
+ })?;
+
+ let names = crates
+ .iter()
+ .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_length = cmp::max(80, 128 - description_margin);
+
+ let descriptions = crates.iter().map(|krate| {
+ krate
+ .description
+ .as_ref()
+ .map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length))
+ });
+
+ 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
+ }
+ None => name,
+ };
+ let mut fragments = line.split(query).peekable();
+ while let Some(fragment) = fragments.next() {
+ let _ = config.shell().write_stdout(fragment, &ColorSpec::new());
+ if fragments.peek().is_some() {
+ let _ = config.shell().write_stdout(
+ query,
+ &ColorSpec::new().set_bold(true).set_fg(Some(Color::Green)),
+ );
+ }
+ }
+ let _ = config.shell().write_stdout("\n", &ColorSpec::new());
+ }
+
+ 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
+ ),
+ &ColorSpec::new(),
+ );
+ } else if total_crates > limit && limit >= search_max_limit {
+ let extra = if source_ids.original.is_crates_io() {
+ let url = Url::parse_with_params("https://crates.io/search", &[("q", query)])?;
+ format!(" (go to {url} to see more)")
+ } else {
+ String::new()
+ };
+ let _ = config.shell().write_stdout(
+ format_args!("... and {} crates more{}\n", total_crates - limit, extra),
+ &ColorSpec::new(),
+ );
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/ops/registry/yank.rs b/src/tools/cargo/src/cargo/ops/registry/yank.rs
new file mode 100644
index 000000000..7f087570a
--- /dev/null
+++ b/src/tools/cargo/src/cargo/ops/registry/yank.rs
@@ -0,0 +1,76 @@
+//! Interacts with the registry [yank] and [unyank] API.
+//!
+//! [yank]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#yank
+//! [unyank]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#unyank
+
+use anyhow::bail;
+use anyhow::Context as _;
+
+use crate::core::Workspace;
+use crate::util::auth;
+use crate::util::auth::Secret;
+use crate::util::config::Config;
+use crate::util::errors::CargoResult;
+use crate::util::important_paths::find_root_manifest_for_wd;
+
+pub fn yank(
+ config: &Config,
+ krate: Option<String>,
+ version: Option<String>,
+ token: Option<Secret<String>>,
+ index: Option<String>,
+ undo: bool,
+ reg: Option<String>,
+) -> CargoResult<()> {
+ let name = match krate {
+ Some(name) => name,
+ None => {
+ let manifest_path = find_root_manifest_for_wd(config.cwd())?;
+ let ws = Workspace::new(&manifest_path, config)?;
+ ws.current()?.package_id().name().to_string()
+ }
+ };
+ let version = match version {
+ Some(v) => v,
+ None => bail!("a version must be specified to yank"),
+ };
+
+ let message = if undo {
+ auth::Mutation::Unyank {
+ name: &name,
+ vers: &version,
+ }
+ } else {
+ auth::Mutation::Yank {
+ name: &name,
+ vers: &version,
+ }
+ };
+
+ let (mut registry, _) = super::registry(
+ config,
+ token.as_ref().map(Secret::as_deref),
+ index.as_deref(),
+ reg.as_deref(),
+ true,
+ Some(message),
+ )?;
+
+ let package_spec = format!("{}@{}", name, version);
+ if undo {
+ config.shell().status("Unyank", package_spec)?;
+ registry.unyank(&name, &version).with_context(|| {
+ format!(
+ "failed to undo a yank from the registry at {}",
+ registry.host()
+ )
+ })?;
+ } else {
+ config.shell().status("Yank", package_spec)?;
+ registry
+ .yank(&name, &version)
+ .with_context(|| format!("failed to yank from the registry at {}", registry.host()))?;
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/sources/config.rs b/src/tools/cargo/src/cargo/sources/config.rs
index 5d5a4e8db..4097567bb 100644
--- a/src/tools/cargo/src/cargo/sources/config.rs
+++ b/src/tools/cargo/src/cargo/sources/config.rs
@@ -14,7 +14,9 @@ use log::debug;
use std::collections::{HashMap, HashSet};
use url::Url;
-/// Represents the entire `[source]` table in Cargo configuration.
+/// Represents the entire [`[source]` replacement table][1] in Cargo configuration.
+///
+/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#source
#[derive(Clone)]
pub struct SourceConfigMap<'cfg> {
/// Mapping of source name to the toml configuration.
diff --git a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
index 9a623151e..7b013f99c 100644
--- a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
+++ b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
@@ -1,5 +1,8 @@
//! SSH host key validation support.
//!
+//! The only public item in this module is [`certificate_check`],
+//! which provides a callback to [`git2::RemoteCallbacks::certificate_check`].
+//!
//! A primary goal with this implementation is to provide user-friendly error
//! messages, guiding them to understand the issue and how to resolve it.
//!
@@ -408,7 +411,7 @@ fn check_ssh_known_hosts_loaded(
// fingerprints (see FingerprintHash ssh config option). Here we only
// support SHA256.
let mut remote_fingerprint = cargo_util::Sha256::new();
- remote_fingerprint.update(remote_host_key.clone());
+ remote_fingerprint.update(remote_host_key);
let remote_fingerprint = STANDARD_NO_PAD.encode(remote_fingerprint.finish());
let remote_host_key_encoded = STANDARD.encode(remote_host_key);
diff --git a/src/tools/cargo/src/cargo/sources/git/mod.rs b/src/tools/cargo/src/cargo/sources/git/mod.rs
index 47827f267..5d9d2886e 100644
--- a/src/tools/cargo/src/cargo/sources/git/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/git/mod.rs
@@ -1,3 +1,12 @@
+//! Home of the [`GitSource`].
+//!
+//! Apparently, the most important type in this module is [`GitSource`].
+//! [`utils`] provides libgit2 utilities like fetch and checkout, whereas
+//! [`oxide`] is the couterpart for gitoxide integration. [`known_hosts`]
+//! is the mitigation of [CVE-2022-46176].
+//!
+//! [CVE-2022-46176]: https://blog.rust-lang.org/2023/01/10/cve-2022-46176.html
+
pub use self::source::GitSource;
pub use self::utils::{fetch, GitCheckout, GitDatabase, GitRemote};
mod known_hosts;
@@ -5,6 +14,7 @@ mod oxide;
mod source;
mod utils;
+/// For `-Zgitoxide` integration.
pub mod fetch {
use crate::core::features::GitoxideFeatures;
use crate::Config;
diff --git a/src/tools/cargo/src/cargo/sources/git/oxide.rs b/src/tools/cargo/src/cargo/sources/git/oxide.rs
index c8d0a4ecd..e86c63e8e 100644
--- a/src/tools/cargo/src/cargo/sources/git/oxide.rs
+++ b/src/tools/cargo/src/cargo/sources/git/oxide.rs
@@ -1,7 +1,7 @@
//! This module contains all code sporting `gitoxide` for operations on `git` repositories and it mirrors
//! `utils` closely for now. One day it can be renamed into `utils` once `git2` isn't required anymore.
-use crate::ops::HttpTimeout;
+use crate::util::network::http::HttpTimeout;
use crate::util::{human_readable_bytes, network, MetricsCounter, Progress};
use crate::{CargoResult, Config};
use cargo_util::paths;
@@ -353,6 +353,8 @@ pub fn cargo_config_to_gitoxide_overrides(config: &Config) -> CargoResult<Vec<BS
Ok(values)
}
+/// Reinitializes a given Git repository. This is useful when a Git repoistory
+/// seems corrupted and we want to start over.
pub fn reinitialize(git_dir: &Path) -> CargoResult<()> {
fn init(path: &Path, bare: bool) -> CargoResult<()> {
let mut opts = git2::RepositoryInitOptions::new();
diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs
index 4cdb8983b..b021d23a0 100644
--- a/src/tools/cargo/src/cargo/sources/git/source.rs
+++ b/src/tools/cargo/src/cargo/sources/git/source.rs
@@ -1,3 +1,5 @@
+//! See [GitSource].
+
use crate::core::source::{MaybePackage, QueryKind, Source, SourceId};
use crate::core::GitReference;
use crate::core::{Dependency, Package, PackageId, Summary};
@@ -13,18 +15,71 @@ use std::fmt::{self, Debug, Formatter};
use std::task::Poll;
use url::Url;
+/// `GitSource` contains one or more packages gathering from a Git repository.
+/// Under the hood it uses [`PathSource`] to discover packages inside the
+/// repository.
+///
+/// ## Filesystem layout
+///
+/// During a successful `GitSource` download, at least two Git repositories are
+/// created: one is the shared Git database of this remote, and the other is the
+/// Git checkout to a specific revision, which contains the actual files to be
+/// compiled. Multiple checkouts can be cloned from a single Git database.
+///
+/// Those repositories are located at Cargo's Git cache directory
+/// `$CARGO_HOME/git`. The file tree of the cache directory roughly looks like:
+///
+/// ```text
+/// $CARGO_HOME/git/
+/// ├── checkouts/
+/// │ ├── gimli-a0d193bd15a5ed96/
+/// │ │ ├── 8e73ef0/ # Git short ID for a certain revision
+/// │ │ ├── a2a4b78/
+/// │ │ └── e33d1ac/
+/// │ ├── log-c58e1db3de7c154d-shallow/
+/// │ │ └── 11eda98/
+/// └── db/
+/// ├── gimli-a0d193bd15a5ed96/
+/// └── log-c58e1db3de7c154d-shallow/
+/// ```
+///
+/// For more on Git cache directory, see ["Cargo Home"] in The Cargo Book.
+///
+/// For more on the directory format `<pkg>-<hash>[-shallow]`, see [`ident`]
+/// and [`ident_shallow`].
+///
+/// ## Locked to a revision
+///
+/// Once a `GitSource` is fetched, it will resolve to a specific commit revision.
+/// This is often mentioned as "locked revision" (`locked_rev`) throughout the
+/// codebase. The revision is written into `Cargo.lock`. This is essential since
+/// we want to ensure a package can compiles with the same set of files when
+/// a `Cargo.lock` is present. With the `locked_rev` provided, `GitSource` can
+/// precisely fetch the same revision from the Git repository.
+///
+/// ["Cargo Home"]: https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#directories
pub struct GitSource<'cfg> {
+ /// The git remote which we're going to fetch from.
remote: GitRemote,
+ /// The Git reference from the manifest file.
manifest_reference: GitReference,
+ /// The revision which a git source is locked to.
+ /// This is expected to be set after the Git repository is fetched.
locked_rev: Option<git2::Oid>,
+ /// The unique identifier of this source.
source_id: SourceId,
+ /// The underlying path source to discover packages inside the Git repository.
path_source: Option<PathSource<'cfg>>,
+ /// The identifer of this source for Cargo's Git cache directory.
+ /// See [`ident`] for more.
ident: String,
config: &'cfg Config,
+ /// Disables status messages.
quiet: bool,
}
impl<'cfg> GitSource<'cfg> {
+ /// Creates a git source for the given [`SourceId`].
pub fn new(source_id: SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> {
assert!(source_id.is_git(), "id is not git, id={}", source_id);
@@ -59,10 +114,14 @@ impl<'cfg> GitSource<'cfg> {
Ok(source)
}
+ /// Gets the remote repository URL.
pub fn url(&self) -> &Url {
self.remote.url()
}
+ /// Returns the packages discovered by this source. It may fetch the Git
+ /// repository as well as walk the filesystem if package informations
+ /// haven't yet updated.
pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> {
if self.path_source.is_none() {
self.invalidate_cache();
@@ -72,7 +131,8 @@ impl<'cfg> GitSource<'cfg> {
}
}
-/// Create an identifier from a URL, essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`.
+/// Create an identifier from a URL,
+/// essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`.
fn ident(id: &SourceId) -> String {
let ident = id
.canonical_url()
@@ -86,10 +146,12 @@ fn ident(id: &SourceId) -> String {
format!("{}-{}", ident, short_hash(id.canonical_url()))
}
-/// Like `ident()`, but appends `-shallow` to it, turning `proto://host/path/repo` into `repo-<hash-of-url>-shallow`.
+/// Like [`ident()`], but appends `-shallow` to it, turning
+/// `proto://host/path/repo` into `repo-<hash-of-url>-shallow`.
///
-/// It's important to separate shallow from non-shallow clones for reasons of backwards compatibility - older
-/// cargo's aren't necessarily handling shallow clones correctly.
+/// It's important to separate shallow from non-shallow clones for reasons of
+/// backwards compatibility --- older cargo's aren't necessarily handling
+/// shallow clones correctly.
fn ident_shallow(id: &SourceId, is_shallow: bool) -> String {
let mut ident = ident(id);
if is_shallow {
@@ -217,8 +279,7 @@ impl<'cfg> Source for GitSource<'cfg> {
.join("checkouts")
.join(&self.ident)
.join(short_id.as_str());
- let parent_remote_url = self.url();
- db.copy_to(actual_rev, &checkout_path, self.config, parent_remote_url)?;
+ db.copy_to(actual_rev, &checkout_path, self.config)?;
let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);
diff --git a/src/tools/cargo/src/cargo/sources/git/utils.rs b/src/tools/cargo/src/cargo/sources/git/utils.rs
index c14d1daf3..0c7ce8b64 100644
--- a/src/tools/cargo/src/cargo/sources/git/utils.rs
+++ b/src/tools/cargo/src/cargo/sources/git/utils.rs
@@ -23,6 +23,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use url::Url;
+/// A file indicates that if present, `git reset` has been done and a repo
+/// checkout is ready to go. See [`GitCheckout::reset`] for why we need this.
+const CHECKOUT_READY_LOCK: &str = ".cargo-ok";
+
fn serialize_str<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error>
where
T: fmt::Display,
@@ -31,60 +35,72 @@ where
s.collect_str(t)
}
+/// A short abbreviated OID.
+///
+/// Exists for avoiding extra allocations in [`GitDatabase::to_short_id`].
pub struct GitShortID(git2::Buf);
impl GitShortID {
+ /// Views the short ID as a `str`.
pub fn as_str(&self) -> &str {
self.0.as_str().unwrap()
}
}
-/// `GitRemote` represents a remote repository. It gets cloned into a local
-/// `GitDatabase`.
+/// A remote repository. It gets cloned into a local [`GitDatabase`].
#[derive(PartialEq, Clone, Debug, Serialize)]
pub struct GitRemote {
+ /// URL to a remote repository.
#[serde(serialize_with = "serialize_str")]
url: Url,
}
-/// `GitDatabase` is a local clone of a remote repository's database. Multiple
-/// `GitCheckouts` can be cloned from this `GitDatabase`.
-#[derive(Serialize)]
+/// A local clone of a remote repository's database. Multiple [`GitCheckout`]s
+/// can be cloned from a single [`GitDatabase`].
pub struct GitDatabase {
+ /// The remote repository where this database is fetched from.
remote: GitRemote,
+ /// Path to the root of the underlying Git repository on the local filesystem.
path: PathBuf,
- #[serde(skip_serializing)]
+ /// Underlying Git repository instance for this database.
repo: git2::Repository,
}
-/// `GitCheckout` is a local checkout of a particular revision. Calling
-/// `clone_into` with a reference will resolve the reference into a revision,
-/// and return an `anyhow::Error` if no revision for that reference was found.
-#[derive(Serialize)]
+/// A local checkout of a particular revision from a [`GitDatabase`].
pub struct GitCheckout<'a> {
+ /// The git database where this checkout is cloned from.
database: &'a GitDatabase,
- location: PathBuf,
- #[serde(serialize_with = "serialize_str")]
+ /// Path to the root of the underlying Git repository on the local filesystem.
+ path: PathBuf,
+ /// The git revision this checkout is for.
revision: git2::Oid,
- #[serde(skip_serializing)]
+ /// Underlying Git repository instance for this checkout.
repo: git2::Repository,
}
-// Implementations
-
impl GitRemote {
+ /// Creates an instance for a remote repository URL.
pub fn new(url: &Url) -> GitRemote {
GitRemote { url: url.clone() }
}
+ /// Gets the remote repository URL.
pub fn url(&self) -> &Url {
&self.url
}
- pub fn rev_for(&self, path: &Path, reference: &GitReference) -> CargoResult<git2::Oid> {
- reference.resolve(&self.db_at(path)?.repo)
- }
-
+ /// Fetches and checkouts to a reference or a revision from this remote
+ /// into a local path.
+ ///
+ /// This ensures that it gets the up-to-date commit when a named reference
+ /// is given (tag, branch, refs/*). Thus, network connection is involved.
+ ///
+ /// When `locked_rev` is provided, it takes precedence over `reference`.
+ ///
+ /// If we have a previous instance of [`GitDatabase`] then fetch into that
+ /// if we can. If that can successfully load our revision then we've
+ /// populated the database with the latest version of `reference`, so
+ /// return that database and the rev we resolve to.
pub fn checkout(
&self,
into: &Path,
@@ -93,10 +109,6 @@ impl GitRemote {
locked_rev: Option<git2::Oid>,
cargo_config: &Config,
) -> CargoResult<(GitDatabase, git2::Oid)> {
- // If we have a previous instance of `GitDatabase` then fetch into that
- // if we can. If that can successfully load our revision then we've
- // populated the database with the latest version of `reference`, so
- // return that database and the rev we resolve to.
let locked_ref = locked_rev.map(|oid| GitReference::Rev(oid.to_string()));
let reference = locked_ref.as_ref().unwrap_or(reference);
if let Some(mut db) = db {
@@ -149,6 +161,7 @@ impl GitRemote {
))
}
+ /// Creates a [`GitDatabase`] of this remote at `db_path`.
pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
let repo = git2::Repository::open(db_path)?;
Ok(GitDatabase {
@@ -160,12 +173,12 @@ impl GitRemote {
}
impl GitDatabase {
+ /// Checkouts to a revision at `dest`ination from this database.
pub fn copy_to(
&self,
rev: git2::Oid,
dest: &Path,
cargo_config: &Config,
- parent_remote_url: &Url,
) -> CargoResult<GitCheckout<'_>> {
// If the existing checkout exists, and it is fresh, use it.
// A non-fresh checkout can happen if the checkout operation was
@@ -173,31 +186,35 @@ impl GitDatabase {
// clone is created.
let checkout = match git2::Repository::open(dest)
.ok()
- .map(|repo| GitCheckout::new(dest, self, rev, repo))
+ .map(|repo| GitCheckout::new(self, rev, repo))
.filter(|co| co.is_fresh())
{
Some(co) => co,
None => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
};
- checkout.update_submodules(cargo_config, parent_remote_url)?;
+ checkout.update_submodules(cargo_config)?;
Ok(checkout)
}
+ /// Get a short OID for a `revision`, usually 7 chars or more if ambiguous.
pub fn to_short_id(&self, revision: git2::Oid) -> CargoResult<GitShortID> {
let obj = self.repo.find_object(revision, None)?;
Ok(GitShortID(obj.short_id()?))
}
+ /// Checks if the database contains the object of this `oid`..
pub fn contains(&self, oid: git2::Oid) -> bool {
self.repo.revparse_single(&oid.to_string()).is_ok()
}
+ /// [`GitReference::resolve`]s this reference with this database.
pub fn resolve(&self, r: &GitReference) -> CargoResult<git2::Oid> {
r.resolve(&self.repo)
}
}
impl GitReference {
+ /// Resolves self to an object ID with objects the `repo` currently has.
pub fn resolve(&self, repo: &git2::Repository) -> CargoResult<git2::Oid> {
let id = match self {
// Note that we resolve the named tag here in sync with where it's
@@ -243,20 +260,32 @@ impl GitReference {
}
impl<'a> GitCheckout<'a> {
+ /// Creates an instance of [`GitCheckout`]. This doesn't imply the checkout
+ /// is done. Use [`GitCheckout::is_fresh`] to check.
+ ///
+ /// * The `database` is where this checkout is from.
+ /// * The `repo` will be the checked out Git repoistory.
fn new(
- path: &Path,
database: &'a GitDatabase,
revision: git2::Oid,
repo: git2::Repository,
) -> GitCheckout<'a> {
+ let path = repo.workdir().unwrap_or_else(|| repo.path());
GitCheckout {
- location: path.to_path_buf(),
+ path: path.to_path_buf(),
database,
revision,
repo,
}
}
+ /// Gets the remote repository URL.
+ fn remote_url(&self) -> &Url {
+ &self.database.remote.url()
+ }
+
+ /// Clone a repo for a `revision` into a local path from a `datatabase`.
+ /// This is a filesystem-to-filesystem clone.
fn clone_into(
into: &Path,
database: &'a GitDatabase,
@@ -293,10 +322,12 @@ impl<'a> GitCheckout<'a> {
.with_checkout(checkout)
.fetch_options(fopts)
.clone(url.as_str(), into)?;
- // `git2` doesn't seem to handle shallow repos correctly when doing a local clone.
- // Fortunately all that's needed is the copy of the one file that defines the
- // shallow boundary, the commits which have their parents omitted as part of the
- // shallow clone.
+ // `git2` doesn't seem to handle shallow repos correctly when doing
+ // a local clone. Fortunately all that's needed is the copy of the
+ // one file that defines the shallow boundary, the commits which
+ // have their parents omitted as part of the shallow clone.
+ //
+ // TODO(git2): remove this when git2 supports shallow clone correctly
if database.repo.is_shallow() {
std::fs::copy(
database.repo.path().join("shallow"),
@@ -308,31 +339,38 @@ impl<'a> GitCheckout<'a> {
})?;
let repo = repo.unwrap();
- let checkout = GitCheckout::new(into, database, revision, repo);
+ let checkout = GitCheckout::new(database, revision, repo);
checkout.reset(config)?;
Ok(checkout)
}
+ /// Checks if the `HEAD` of this checkout points to the expected revision.
fn is_fresh(&self) -> bool {
match self.repo.revparse_single("HEAD") {
Ok(ref head) if head.id() == self.revision => {
// See comments in reset() for why we check this
- self.location.join(".cargo-ok").exists()
+ self.path.join(CHECKOUT_READY_LOCK).exists()
}
_ => false,
}
}
+ /// Similar to [`reset()`]. This roughly performs `git reset --hard` to the
+ /// revision of this checkout, with additional interrupt protection by a
+ /// dummy file [`CHECKOUT_READY_LOCK`].
+ ///
+ /// If we're interrupted while performing a `git reset` (e.g., we die
+ /// because of a signal) Cargo needs to be sure to try to check out this
+ /// repo again on the next go-round.
+ ///
+ /// To enable this we have a dummy file in our checkout, [`.cargo-ok`],
+ /// which if present means that the repo has been successfully reset and is
+ /// ready to go. Hence if we start to do a reset, we make sure this file
+ /// *doesn't* exist, and then once we're done we create the file.
+ ///
+ /// [`.cargo-ok`]: CHECKOUT_READY_LOCK
fn reset(&self, config: &Config) -> CargoResult<()> {
- // If we're interrupted while performing this reset (e.g., we die because
- // of a signal) Cargo needs to be sure to try to check out this repo
- // again on the next go-round.
- //
- // To enable this we have a dummy file in our checkout, .cargo-ok, which
- // if present means that the repo has been successfully reset and is
- // ready to go. Hence if we start to do a reset, we make sure this file
- // *doesn't* exist, and then once we're done we create the file.
- let ok_file = self.location.join(".cargo-ok");
+ let ok_file = self.path.join(CHECKOUT_READY_LOCK);
let _ = paths::remove_file(&ok_file);
info!("reset {} to {}", self.repo.path().display(), self.revision);
@@ -347,13 +385,20 @@ impl<'a> GitCheckout<'a> {
Ok(())
}
- fn update_submodules(&self, cargo_config: &Config, parent_remote_url: &Url) -> CargoResult<()> {
- return update_submodules(&self.repo, cargo_config, parent_remote_url);
+ /// Like `git submodule update --recursive` but for this git checkout.
+ ///
+ /// This function respects `submodule.<name>.update = none`[^1] git config.
+ /// Submodules set to `none` won't be fetched.
+ ///
+ /// [^1]: <https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-none>
+ fn update_submodules(&self, cargo_config: &Config) -> CargoResult<()> {
+ return update_submodules(&self.repo, cargo_config, self.remote_url().as_str());
+ /// Recusive helper for [`GitCheckout::update_submodules`].
fn update_submodules(
repo: &git2::Repository,
cargo_config: &Config,
- parent_remote_url: &Url,
+ parent_remote_url: &str,
) -> CargoResult<()> {
debug!("update submodules for: {:?}", repo.workdir().unwrap());
@@ -370,11 +415,12 @@ impl<'a> GitCheckout<'a> {
Ok(())
}
+ /// Update a single Git submodule, and recurse into its submodules.
fn update_submodule(
parent: &git2::Repository,
child: &mut git2::Submodule<'_>,
cargo_config: &Config,
- parent_remote_url: &Url,
+ parent_remote_url: &str,
) -> CargoResult<()> {
child.init(false)?;
@@ -394,31 +440,7 @@ impl<'a> GitCheckout<'a> {
return Ok(());
}
- // Git only assumes a URL is a relative path if it starts with `./` or `../`.
- // See [`git submodule add`] documentation.
- //
- // [`git submodule add`]: https://git-scm.com/docs/git-submodule
- let url = if child_url_str.starts_with("./") || child_url_str.starts_with("../") {
- let mut new_parent_remote_url = parent_remote_url.clone();
-
- let mut new_path = Cow::from(parent_remote_url.path());
- if !new_path.ends_with('/') {
- new_path.to_mut().push('/');
- }
- new_parent_remote_url.set_path(&new_path);
-
- match new_parent_remote_url.join(child_url_str) {
- Ok(x) => x.to_string(),
- Err(err) => Err(err).with_context(|| {
- format!(
- "failed to parse relative child submodule url `{}` using parent base url `{}`",
- child_url_str, new_parent_remote_url
- )
- })?,
- }
- } else {
- child_url_str.to_string()
- };
+ let child_remote_url = absolute_submodule_url(parent_remote_url, child_url_str)?;
// A submodule which is listed in .gitmodules but not actually
// checked out will not have a head id, so we should ignore it.
@@ -438,7 +460,7 @@ impl<'a> GitCheckout<'a> {
let mut repo = match head_and_repo {
Ok((head, repo)) => {
if child.head_id() == head {
- return update_submodules(&repo, cargo_config, parent_remote_url);
+ return update_submodules(&repo, cargo_config, &child_remote_url);
}
repo
}
@@ -452,29 +474,78 @@ impl<'a> GitCheckout<'a> {
let reference = GitReference::Rev(head.to_string());
cargo_config
.shell()
- .status("Updating", format!("git submodule `{}`", url))?;
+ .status("Updating", format!("git submodule `{child_remote_url}`"))?;
fetch(
&mut repo,
- &url,
+ &child_remote_url,
&reference,
cargo_config,
RemoteKind::GitDependency,
)
.with_context(|| {
- format!(
- "failed to fetch submodule `{}` from {}",
- child.name().unwrap_or(""),
- url
- )
+ let name = child.name().unwrap_or("");
+ format!("failed to fetch submodule `{name}` from {child_remote_url}",)
})?;
let obj = repo.find_object(head, None)?;
reset(&repo, &obj, cargo_config)?;
- update_submodules(&repo, cargo_config, parent_remote_url)
+ update_submodules(&repo, cargo_config, &child_remote_url)
}
}
}
+/// Constructs an absolute URL for a child submodule URL with its parent base URL.
+///
+/// Git only assumes a submodule URL is a relative path if it starts with `./`
+/// or `../` [^1]. To fetch the correct repo, we need to construct an absolute
+/// submodule URL.
+///
+/// At this moment it comes with some limitations:
+///
+/// * GitHub doesn't accept non-normalized URLs with relative paths.
+/// (`ssh://git@github.com/rust-lang/cargo.git/relative/..` is invalid)
+/// * `url` crate cannot parse SCP-like URLs.
+/// (`git@github.com:rust-lang/cargo.git` is not a valid WHATWG URL)
+///
+/// To overcome these, this patch always tries [`Url::parse`] first to normalize
+/// the path. If it couldn't, append the relative path as the last resort and
+/// pray the remote git service supports non-normalized URLs.
+///
+/// See also rust-lang/cargo#12404 and rust-lang/cargo#12295.
+///
+/// [^1]: <https://git-scm.com/docs/git-submodule>
+fn absolute_submodule_url<'s>(base_url: &str, submodule_url: &'s str) -> CargoResult<Cow<'s, str>> {
+ let absolute_url = if ["./", "../"].iter().any(|p| submodule_url.starts_with(p)) {
+ match Url::parse(base_url) {
+ Ok(mut base_url) => {
+ let path = base_url.path();
+ if !path.ends_with('/') {
+ base_url.set_path(&format!("{path}/"));
+ }
+ let absolute_url = base_url.join(submodule_url).with_context(|| {
+ format!(
+ "failed to parse relative child submodule url `{submodule_url}` \
+ using parent base url `{base_url}`"
+ )
+ })?;
+ Cow::from(absolute_url.to_string())
+ }
+ Err(_) => {
+ let mut absolute_url = base_url.to_string();
+ if !absolute_url.ends_with('/') {
+ absolute_url.push('/');
+ }
+ absolute_url.push_str(submodule_url);
+ Cow::from(absolute_url)
+ }
+ }
+ } else {
+ Cow::from(submodule_url)
+ };
+
+ Ok(absolute_url)
+}
+
/// Prepare the authentication callbacks for cloning a git repository.
///
/// The main purpose of this function is to construct the "authentication
@@ -745,6 +816,9 @@ where
Err(err)
}
+/// `git reset --hard` to the given `obj` for the `repo`.
+///
+/// The `obj` is a commit-ish to which the head should be moved.
fn reset(repo: &git2::Repository, obj: &git2::Object<'_>, config: &Config) -> CargoResult<()> {
let mut pb = Progress::new("Checkout", config);
let mut opts = git2::build::CheckoutBuilder::new();
@@ -757,6 +831,14 @@ fn reset(repo: &git2::Repository, obj: &git2::Object<'_>, config: &Config) -> Ca
Ok(())
}
+/// Prepares the callbacks for fetching a git repository.
+///
+/// The main purpose of this function is to construct everything before a fetch.
+/// This will attempt to setup a progress bar, the authentication for git,
+/// ssh known hosts check, and the network retry mechanism.
+///
+/// The callback is provided a fetch options, which can be used by the actual
+/// git fetch.
pub fn with_fetch_options(
git_config: &git2::Config,
url: &str,
@@ -831,12 +913,22 @@ pub fn with_fetch_options(
})
}
-/// Note that `history` is a complex computed value to determine whether it's acceptable to perform shallow clones
-/// at all. It's needed to allow the caller to determine the correct position of the destination repository or move it
-/// into place should its position change.
+/// Attempts to fetch the given git `reference` for a Git repository.
+///
+/// This is the main entry for git clone/fetch. It does the followings:
+///
+/// * Turns [`GitReference`] into refspecs accordingly.
+/// * Dispatches `git fetch` using libgit2, gitoxide, or git CLI.
+///
+/// The `remote_url` argument is the git remote URL where we want to fetch from.
+///
+/// The `remote_kind` argument is a thing for [`-Zgitoxide`] shallow clones
+/// at this time. It could be extended when libgit2 supports shallow clones.
+///
+/// [`-Zgitoxide`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gitoxide
pub fn fetch(
repo: &mut git2::Repository,
- orig_url: &str,
+ remote_url: &str,
reference: &GitReference,
config: &Config,
remote_kind: RemoteKind,
@@ -853,9 +945,7 @@ pub fn fetch(
let shallow = remote_kind.to_shallow_setting(repo.is_shallow(), config);
- // If we're fetching from GitHub, attempt GitHub's special fast path for
- // testing if we've already got an up-to-date copy of the repository.
- let oid_to_fetch = match github_fast_path(repo, orig_url, reference, config) {
+ let oid_to_fetch = match github_fast_path(repo, remote_url, reference, config) {
Ok(FastPathRev::UpToDate) => return Ok(()),
Ok(FastPathRev::NeedsFetch(rev)) => Some(rev),
Ok(FastPathRev::Indeterminate) => None,
@@ -865,10 +955,6 @@ pub fn fetch(
}
};
- // We reuse repositories quite a lot, so before we go through and update the
- // repo check to see if it's a little too old and could benefit from a gc.
- // In theory this shouldn't be too expensive compared to the network
- // request we're about to issue.
maybe_gc_repo(repo, config)?;
clean_repo_temp_files(repo);
@@ -921,15 +1007,10 @@ pub fn fetch(
}
}
- // Unfortunately `libgit2` is notably lacking in the realm of authentication
- // when compared to the `git` command line. As a result, allow an escape
- // hatch for users that would prefer to use `git`-the-CLI for fetching
- // repositories instead of `libgit2`-the-library. This should make more
- // flavors of authentication possible while also still giving us all the
- // speed and portability of using `libgit2`.
if let Some(true) = config.net_config()?.git_fetch_with_cli {
- return fetch_with_cli(repo, orig_url, &refspecs, tags, config);
+ return fetch_with_cli(repo, remote_url, &refspecs, tags, config);
}
+
if config
.cli_unstable()
.gitoxide
@@ -963,10 +1044,10 @@ pub fn fetch(
)
.map_err(crate::sources::git::fetch::Error::from)
.and_then(|repo| {
- debug!("initiating fetch of {:?} from {}", refspecs, orig_url);
+ debug!("initiating fetch of {refspecs:?} from {remote_url}");
let url_for_authentication = &mut *url_for_authentication;
let remote = repo
- .remote_at(orig_url)?
+ .remote_at(remote_url)?
.with_fetch_tags(if tags {
gix::remote::fetch::Tags::All
} else {
@@ -985,10 +1066,9 @@ pub fn fetch(
let mut authenticate = connection.configured_credentials(url)?;
let connection = connection.with_credentials(
move |action: gix::protocol::credentials::helper::Action| {
- if let Some(url) = action
- .context()
- .and_then(|ctx| ctx.url.as_ref().filter(|url| *url != orig_url))
- {
+ if let Some(url) = action.context().and_then(|ctx| {
+ ctx.url.as_ref().filter(|url| *url != remote_url)
+ }) {
url_for_authentication(url.as_ref());
}
authenticate(action)
@@ -1034,9 +1114,9 @@ pub fn fetch(
}
res
} else {
- debug!("doing a fetch for {}", orig_url);
+ debug!("doing a fetch for {remote_url}");
let git_config = git2::Config::open_default()?;
- with_fetch_options(&git_config, orig_url, config, &mut |mut opts| {
+ with_fetch_options(&git_config, remote_url, config, &mut |mut opts| {
if tags {
opts.download_tags(git2::AutotagOption::All);
}
@@ -1052,10 +1132,10 @@ pub fn fetch(
// blown away the repository, then we want to return the error as-is.
let mut repo_reinitialized = false;
loop {
- debug!("initiating fetch of {:?} from {}", refspecs, orig_url);
- let res = repo
- .remote_anonymous(orig_url)?
- .fetch(&refspecs, Some(&mut opts), None);
+ debug!("initiating fetch of {refspecs:?} from {remote_url}");
+ let res =
+ repo.remote_anonymous(remote_url)?
+ .fetch(&refspecs, Some(&mut opts), None);
let err = match res {
Ok(()) => break,
Err(e) => e,
@@ -1093,6 +1173,17 @@ fn has_shallow_lock_file(err: &crate::sources::git::fetch::Error) -> bool {
)
}
+/// Attempts to use `git` CLI installed on the system to fetch a repository,
+/// when the config value [`net.git-fetch-with-cli`][1] is set.
+///
+/// Unfortunately `libgit2` is notably lacking in the realm of authentication
+/// when compared to the `git` command line. As a result, allow an escape
+/// hatch for users that would prefer to use `git`-the-CLI for fetching
+/// repositories instead of `libgit2`-the-library. This should make more
+/// flavors of authentication possible while also still giving us all the
+/// speed and portability of using `libgit2`.
+///
+/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#netgit-fetch-with-cli
fn fetch_with_cli(
repo: &mut git2::Repository,
url: &str,
@@ -1137,6 +1228,8 @@ fn fetch_with_cli(
Ok(())
}
+/// Attempts to `git gc` a repository.
+///
/// Cargo has a bunch of long-lived git repositories in its global cache and
/// some, like the index, are updated very frequently. Right now each update
/// creates a new "pack file" inside the git database, and over time this can
@@ -1144,12 +1237,17 @@ fn fetch_with_cli(
///
/// One pathological use case today is where libgit2 opens hundreds of file
/// descriptors, getting us dangerously close to blowing out the OS limits of
-/// how many fds we can have open. This is detailed in #4403.
+/// how many fds we can have open. This is detailed in [#4403].
///
/// To try to combat this problem we attempt a `git gc` here. Note, though, that
/// we may not even have `git` installed on the system! As a result we
/// opportunistically try a `git gc` when the pack directory looks too big, and
/// failing that we just blow away the repository and start over.
+///
+/// In theory this shouldn't be too expensive compared to the network request
+/// we're about to issue.
+///
+/// [#4403]: https://github.com/rust-lang/cargo/issues/4403
fn maybe_gc_repo(repo: &mut git2::Repository, config: &Config) -> CargoResult<()> {
// Here we arbitrarily declare that if you have more than 100 files in your
// `pack` folder that we need to do a gc.
@@ -1236,6 +1334,8 @@ fn clean_repo_temp_files(repo: &git2::Repository) {
}
}
+/// Reinitializes a given Git repository. This is useful when a Git repoistory
+/// seems corrupted and we want to start over.
fn reinitialize(repo: &mut git2::Repository) -> CargoResult<()> {
// Here we want to drop the current repository object pointed to by `repo`,
// so we initialize temporary repository in a sub-folder, blow away the
@@ -1259,6 +1359,7 @@ fn reinitialize(repo: &mut git2::Repository) -> CargoResult<()> {
Ok(())
}
+/// Initializes a Git repository at `path`.
fn init(path: &Path, bare: bool) -> CargoResult<git2::Repository> {
let mut opts = git2::RepositoryInitOptions::new();
// Skip anything related to templates, they just call all sorts of issues as
@@ -1269,6 +1370,7 @@ fn init(path: &Path, bare: bool) -> CargoResult<git2::Repository> {
Ok(git2::Repository::init_opts(&path, &opts)?)
}
+/// The result of GitHub fast path check. See [`github_fast_path`] for more.
enum FastPathRev {
/// The local rev (determined by `reference.resolve(repo)`) is already up to
/// date with what this rev resolves to on GitHub's server.
@@ -1281,20 +1383,19 @@ enum FastPathRev {
Indeterminate,
}
+/// Attempts GitHub's special fast path for testing if we've already got an
+/// up-to-date copy of the repository.
+///
/// Updating the index is done pretty regularly so we want it to be as fast as
/// possible. For registries hosted on GitHub (like the crates.io index) there's
-/// a fast path available to use [1] to tell us that there's no updates to be
+/// a fast path available to use[^1] to tell us that there's no updates to be
/// made.
///
-/// This function will attempt to hit that fast path and verify that the `oid`
-/// is actually the current branch of the repository.
-///
-/// [1]: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
-///
/// Note that this function should never cause an actual failure because it's
-/// just a fast path. As a result all errors are ignored in this function and we
-/// just return a `bool`. Any real errors will be reported through the normal
-/// update path above.
+/// just a fast path. As a result, a caller should ignore `Err` returned from
+/// this function and move forward on the normal path.
+///
+/// [^1]: <https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference>
fn github_fast_path(
repo: &mut git2::Repository,
url: &str,
@@ -1406,14 +1507,17 @@ fn github_fast_path(
}
}
+/// Whether a `url` is one from GitHub.
fn is_github(url: &Url) -> bool {
url.host_str() == Some("github.com")
}
+/// Whether a `rev` looks like a commit hash (ASCII hex digits).
fn looks_like_commit_hash(rev: &str) -> bool {
rev.len() >= 7 && rev.chars().all(|ch| ch.is_ascii_hexdigit())
}
+/// Whether `rev` is a shorter hash of `oid`.
fn is_short_hash_of(rev: &str, oid: Oid) -> bool {
let long_hash = oid.to_string();
match long_hash.get(..rev.len()) {
@@ -1421,3 +1525,102 @@ fn is_short_hash_of(rev: &str, oid: Oid) -> bool {
None => false,
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::absolute_submodule_url;
+
+ #[test]
+ fn test_absolute_submodule_url() {
+ let cases = [
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "git@github.com:rust-lang/cargo.git",
+ "git@github.com:rust-lang/cargo.git",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "./",
+ "ssh://git@gitub.com/rust-lang/cargo/",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "../",
+ "ssh://git@gitub.com/rust-lang/",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "./foo",
+ "ssh://git@gitub.com/rust-lang/cargo/foo",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo/",
+ "./foo",
+ "ssh://git@gitub.com/rust-lang/cargo/foo",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo/",
+ "../foo",
+ "ssh://git@gitub.com/rust-lang/foo",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "../foo",
+ "ssh://git@gitub.com/rust-lang/foo",
+ ),
+ (
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "../foo/bar/../baz",
+ "ssh://git@gitub.com/rust-lang/foo/baz",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "ssh://git@gitub.com/rust-lang/cargo",
+ "ssh://git@gitub.com/rust-lang/cargo",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "./",
+ "git@github.com:rust-lang/cargo.git/./",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "../",
+ "git@github.com:rust-lang/cargo.git/../",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "./foo",
+ "git@github.com:rust-lang/cargo.git/./foo",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git/",
+ "./foo",
+ "git@github.com:rust-lang/cargo.git/./foo",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "../foo",
+ "git@github.com:rust-lang/cargo.git/../foo",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git/",
+ "../foo",
+ "git@github.com:rust-lang/cargo.git/../foo",
+ ),
+ (
+ "git@github.com:rust-lang/cargo.git",
+ "../foo/bar/../baz",
+ "git@github.com:rust-lang/cargo.git/../foo/bar/../baz",
+ ),
+ ];
+
+ for (base_url, submodule_url, expected) in cases {
+ let url = absolute_submodule_url(base_url, submodule_url).unwrap();
+ assert_eq!(
+ expected, url,
+ "base `{base_url}`; submodule `{submodule_url}`"
+ );
+ }
+ }
+}
diff --git a/src/tools/cargo/src/cargo/sources/path.rs b/src/tools/cargo/src/cargo/sources/path.rs
index 2f147b19e..bb40ec9b1 100644
--- a/src/tools/cargo/src/cargo/sources/path.rs
+++ b/src/tools/cargo/src/cargo/sources/path.rs
@@ -95,7 +95,7 @@ impl<'cfg> PathSource<'cfg> {
}
/// Returns the packages discovered by this source. It may walk the
- /// the filesystem if package informations haven't yet updated.
+ /// filesystem if package informations haven't yet updated.
pub fn read_packages(&self) -> CargoResult<Vec<Package>> {
if self.updated {
Ok(self.packages.clone())
diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs
index 723c55ffd..a85d87177 100644
--- a/src/tools/cargo/src/cargo/sources/registry/download.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/download.rs
@@ -1,13 +1,15 @@
+//! Shared download logic between [`HttpRegistry`] and [`RemoteRegistry`].
+//!
+//! [`HttpRegistry`]: super::http_remote::HttpRegistry
+//! [`RemoteRegistry`]: super::remote::RemoteRegistry
+
use anyhow::Context;
+use cargo_util::registry::make_dep_path;
use cargo_util::Sha256;
use crate::core::PackageId;
-use crate::sources::registry::make_dep_prefix;
use crate::sources::registry::MaybeLock;
-use crate::sources::registry::{
- RegistryConfig, CHECKSUM_TEMPLATE, CRATE_TEMPLATE, LOWER_PREFIX_TEMPLATE, PREFIX_TEMPLATE,
- VERSION_TEMPLATE,
-};
+use crate::sources::registry::RegistryConfig;
use crate::util::auth;
use crate::util::errors::CargoResult;
use crate::util::{Config, Filesystem};
@@ -17,10 +19,16 @@ use std::io::prelude::*;
use std::io::SeekFrom;
use std::str;
-pub(super) fn filename(pkg: PackageId) -> String {
- format!("{}-{}.crate", pkg.name(), pkg.version())
-}
+const CRATE_TEMPLATE: &str = "{crate}";
+const VERSION_TEMPLATE: &str = "{version}";
+const PREFIX_TEMPLATE: &str = "{prefix}";
+const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
+const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
+/// Checks if `pkg` is downloaded and ready under the directory at `cache_path`.
+/// If not, returns a URL to download it from.
+///
+/// This is primarily called by [`RegistryData::download`](super::RegistryData::download).
pub(super) fn download(
cache_path: &Filesystem,
config: &Config,
@@ -28,8 +36,7 @@ pub(super) fn download(
checksum: &str,
registry_config: RegistryConfig,
) -> CargoResult<MaybeLock> {
- let filename = filename(pkg);
- let path = cache_path.join(&filename);
+ let path = cache_path.join(&pkg.tarball_name());
let path = config.assert_package_cache_locked(&path);
// Attempt to open a read-only copy first to avoid an exclusive write
@@ -61,7 +68,7 @@ pub(super) fn download(
)
.unwrap();
} else {
- let prefix = make_dep_prefix(&*pkg.name());
+ let prefix = make_dep_path(&pkg.name(), true);
url = url
.replace(CRATE_TEMPLATE, &*pkg.name())
.replace(VERSION_TEMPLATE, &pkg.version().to_string())
@@ -83,6 +90,10 @@ pub(super) fn download(
})
}
+/// Verifies the integrity of `data` with `checksum` and persists it under the
+/// directory at `cache_path`.
+///
+/// This is primarily called by [`RegistryData::finish_download`](super::RegistryData::finish_download).
pub(super) fn finish_download(
cache_path: &Filesystem,
config: &Config,
@@ -96,9 +107,8 @@ pub(super) fn finish_download(
anyhow::bail!("failed to verify the checksum of `{}`", pkg)
}
- let filename = filename(pkg);
cache_path.create_dir()?;
- let path = cache_path.join(&filename);
+ let path = cache_path.join(&pkg.tarball_name());
let path = config.assert_package_cache_locked(&path);
let mut dst = OpenOptions::new()
.create(true)
@@ -116,12 +126,16 @@ pub(super) fn finish_download(
Ok(dst)
}
+/// Checks if a tarball of `pkg` has been already downloaded under the
+/// directory at `cache_path`.
+///
+/// This is primarily called by [`RegistryData::is_crate_downloaded`](super::RegistryData::is_crate_downloaded).
pub(super) fn is_crate_downloaded(
cache_path: &Filesystem,
config: &Config,
pkg: PackageId,
) -> bool {
- let path = cache_path.join(filename(pkg));
+ let path = cache_path.join(pkg.tarball_name());
let path = config.assert_package_cache_locked(&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 7e1f2a587..c69ef8f9b 100644
--- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
@@ -1,21 +1,19 @@
-//! Access to a HTTP-based crate registry.
-//!
-//! See [`HttpRegistry`] for details.
+//! Access to a HTTP-based crate registry. See [`HttpRegistry`] for details.
use crate::core::{PackageId, SourceId};
-use crate::ops::{self};
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
+use crate::util::network::http::http_handle;
use crate::util::network::retry::{Retry, RetryResult};
use crate::util::network::sleep::SleepTracker;
use crate::util::{auth, Config, Filesystem, IntoUrl, Progress, ProgressStyle};
use anyhow::Context;
use cargo_util::paths;
-use curl::easy::{Easy, HttpVersion, List};
+use curl::easy::{Easy, List};
use curl::multi::{EasyHandle, Multi};
-use log::{debug, trace, warn};
+use log::{debug, trace};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
@@ -52,8 +50,15 @@ const UNKNOWN: &'static str = "Unknown";
///
/// [RFC 2789]: https://github.com/rust-lang/rfcs/pull/2789
pub struct HttpRegistry<'cfg> {
+ /// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`).
+ ///
+ /// To be fair, `HttpRegistry` doesn't store the registry index it
+ /// downloads on the file system, but other cached data like registry
+ /// configuration could be stored here.
index_path: Filesystem,
+ /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/cache/$REG-HASH`).
cache_path: Filesystem,
+ /// The unique identifier of this registry source.
source_id: SourceId,
config: &'cfg Config,
@@ -95,20 +100,20 @@ pub struct HttpRegistry<'cfg> {
quiet: bool,
}
-/// Helper for downloading crates.
+/// State for currently pending index file downloads.
struct Downloads<'cfg> {
/// When a download is started, it is added to this map. The key is a
- /// "token" (see `Download::token`). It is removed once the download is
+ /// "token" (see [`Download::token`]). It is removed once the download is
/// finished.
pending: HashMap<usize, (Download<'cfg>, EasyHandle)>,
/// Set of paths currently being downloaded.
- /// This should stay in sync with `pending`.
+ /// This should stay in sync with the `pending` field.
pending_paths: HashSet<PathBuf>,
/// Downloads that have failed and are waiting to retry again later.
sleeping: SleepTracker<(Download<'cfg>, Easy)>,
/// The final result of each download.
results: HashMap<PathBuf, CargoResult<CompletedDownload>>,
- /// The next ID to use for creating a token (see `Download::token`).
+ /// The next ID to use for creating a token (see [`Download::token`]).
next: usize,
/// Progress bar.
progress: RefCell<Option<Progress<'cfg>>>,
@@ -119,9 +124,10 @@ struct Downloads<'cfg> {
blocking_calls: usize,
}
+/// Represents a single index file download, including its progress and retry.
struct Download<'cfg> {
- /// The token for this download, used as the key of the `Downloads::pending` map
- /// and stored in `EasyHandle` as well.
+ /// The token for this download, used as the key of the
+ /// [`Downloads::pending`] map and stored in [`EasyHandle`] as well.
token: usize,
/// The path of the package that we're downloading.
@@ -137,14 +143,17 @@ struct Download<'cfg> {
retry: Retry<'cfg>,
}
+/// HTTPS headers [`HttpRegistry`] cares about.
#[derive(Default)]
struct Headers {
last_modified: Option<String>,
etag: Option<String>,
www_authenticate: Vec<String>,
+ /// We don't care about these headers. Put them here for debugging purpose.
others: Vec<String>,
}
+/// HTTP status code [`HttpRegistry`] cares about.
enum StatusCode {
Success,
NotModified,
@@ -152,6 +161,10 @@ enum StatusCode {
Unauthorized,
}
+/// Represents a complete [`Download`] from an HTTP request.
+///
+/// Usually it is constructed in [`HttpRegistry::handle_completed_downloads`],
+/// and then returns to the caller of [`HttpRegistry::load()`].
struct CompletedDownload {
response_code: StatusCode,
data: Vec<u8>,
@@ -159,6 +172,10 @@ struct CompletedDownload {
}
impl<'cfg> HttpRegistry<'cfg> {
+ /// Creates a HTTP-rebased remote registry for `source_id`.
+ ///
+ /// * `name` --- Name of a path segment where `.crate` tarballs and the
+ /// registry index are stored. Expect to be unique.
pub fn new(
source_id: SourceId,
config: &'cfg Config,
@@ -208,6 +225,7 @@ impl<'cfg> HttpRegistry<'cfg> {
})
}
+ /// Splits HTTP `HEADER: VALUE` to a tuple.
fn handle_http_header(buf: &[u8]) -> Option<(&str, &str)> {
if buf.is_empty() {
return None;
@@ -222,6 +240,9 @@ impl<'cfg> HttpRegistry<'cfg> {
Some((tag, value))
}
+ /// Setup the necessary works before the first fetch gets started.
+ ///
+ /// This is a no-op if called more than one time.
fn start_fetch(&mut self) -> CargoResult<()> {
if self.fetch_started {
// We only need to run the setup code once.
@@ -249,6 +270,8 @@ impl<'cfg> HttpRegistry<'cfg> {
Ok(())
}
+ /// Checks the results inside the [`HttpRegistry::multi`] handle, and
+ /// updates relevant state in [`HttpRegistry::downloads`] accordingly.
fn handle_completed_downloads(&mut self) -> CargoResult<()> {
assert_eq!(
self.downloads.pending.len(),
@@ -322,11 +345,15 @@ impl<'cfg> HttpRegistry<'cfg> {
Ok(())
}
+ /// Constructs the full URL to download a index file.
fn full_url(&self, path: &Path) -> String {
// self.url always ends with a slash.
format!("{}{}", self.url, path.display())
}
+ /// Check if an index file of `path` is up-to-date.
+ ///
+ /// The `path` argument is the same as in [`RegistryData::load`].
fn is_fresh(&self, path: &Path) -> bool {
if !self.requested_update {
trace!(
@@ -356,7 +383,7 @@ impl<'cfg> HttpRegistry<'cfg> {
}
let config_json_path = self
.assert_index_locked(&self.index_path)
- .join("config.json");
+ .join(RegistryConfig::NAME);
match fs::read(&config_json_path) {
Ok(raw_data) => match serde_json::from_slice(&raw_data) {
Ok(json) => {
@@ -373,16 +400,16 @@ impl<'cfg> HttpRegistry<'cfg> {
Ok(self.registry_config.as_ref())
}
- /// Get the registry configuration.
+ /// Get the registry configuration from either cache or remote.
fn config(&mut self) -> Poll<CargoResult<&RegistryConfig>> {
debug!("loading config");
let index_path = self.assert_index_locked(&self.index_path);
- let config_json_path = index_path.join("config.json");
- if self.is_fresh(Path::new("config.json")) && self.config_cached()?.is_some() {
+ let config_json_path = index_path.join(RegistryConfig::NAME);
+ if self.is_fresh(Path::new(RegistryConfig::NAME)) && self.config_cached()?.is_some() {
return Poll::Ready(Ok(self.registry_config.as_ref().unwrap()));
}
- match ready!(self.load(Path::new(""), Path::new("config.json"), None)?) {
+ match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) {
LoadResponse::Data {
raw_data,
index_version: _,
@@ -405,6 +432,7 @@ impl<'cfg> HttpRegistry<'cfg> {
}
}
+ /// Moves failed [`Download`]s that are ready to retry to the pending queue.
fn add_sleepers(&mut self) -> CargoResult<()> {
for (dl, handle) in self.downloads.sleeping.to_retry() {
let mut handle = self.multi.add(handle)?;
@@ -515,7 +543,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
StatusCode::Unauthorized
if !self.auth_required
- && path == Path::new("config.json")
+ && path == Path::new(RegistryConfig::NAME)
&& self.config.cli_unstable().registry_auth =>
{
debug!("re-attempting request for config.json with authorization included.");
@@ -565,7 +593,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
}
- if path != Path::new("config.json") {
+ if path != Path::new(RegistryConfig::NAME) {
self.auth_required = ready!(self.config()?).auth_required;
} else if !self.auth_required {
// Check if there's a cached config that says auth is required.
@@ -582,7 +610,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
// Looks like we're going to have to do a network request.
self.start_fetch()?;
- let mut handle = ops::http_handle(self.config)?;
+ let mut handle = http_handle(self.config)?;
let full_url = self.full_url(path);
debug!("fetch {}", full_url);
handle.get(true)?;
@@ -590,20 +618,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
handle.follow_location(true)?;
// Enable HTTP/2 if possible.
- if self.multiplexing {
- crate::try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2");
- } else {
- handle.http_version(HttpVersion::V11)?;
- }
-
- // This is an option to `libcurl` which indicates that if there's a
- // bunch of parallel requests to the same host they all wait until the
- // pipelining status of the host is known. This means that we won't
- // initiate dozens of connections to crates.io, but rather only one.
- // Once the main one is opened we realized that pipelining is possible
- // and multiplexing is possible with static.crates.io. All in all this
- // reduces the number of connections done to a more manageable state.
- crate::try_old_curl!(handle.pipewait(true), "pipewait");
+ crate::try_old_curl_http2_pipewait!(self.multiplexing, handle);
let mut headers = List::new();
// Include a header to identify the protocol. This allows the server to
@@ -790,6 +805,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
impl<'cfg> Downloads<'cfg> {
+ /// Updates the state of the progress bar for downloads.
fn tick(&self) -> CargoResult<()> {
let mut progress = self.progress.borrow_mut();
let Some(progress) = progress.as_mut() else { return Ok(()); };
diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs
index d857a053e..6d565da8f 100644
--- a/src/tools/cargo/src/cargo/sources/registry/index.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/index.rs
@@ -1,11 +1,26 @@
-//! Management of the index of a registry source
+//! Management of the index of a registry source.
//!
//! This module contains management of the index and various operations, such as
//! actually parsing the index, looking for crates, etc. This is intended to be
-//! abstract over remote indices (downloaded via git) and local registry indices
-//! (which are all just present on the filesystem).
+//! abstract over remote indices (downloaded via Git or HTTP) and local registry
+//! indices (which are all just present on the filesystem).
//!
-//! ## Index Performance
+//! ## How the index works
+//!
+//! Here is a simple flow when loading a [`Summary`] (metadata) from the index:
+//!
+//! 1. A query is fired via [`RegistryIndex::query_inner`].
+//! 2. Tries loading all summaries via [`RegistryIndex::load_summaries`], and
+//! under the hood calling [`Summaries::parse`] to parse an index file.
+//! 1. If an on-disk index cache is present, loads it via
+//! [`Summaries::parse_cache`].
+//! 2. Otherwise goes to the slower path [`RegistryData::load`] to get the
+//! specific index file.
+//! 3. A [`Summary`] is now ready in callback `f` in [`RegistryIndex::query_inner`].
+//!
+//! This is just an overview. To know the rationale behind, continue reading.
+//!
+//! ## A layer of on-disk index cache for performance
//!
//! One important aspect of the index is that we want to optimize the "happy
//! path" as much as possible. Whenever you type `cargo build` Cargo will
@@ -20,19 +35,20 @@
//! don't need them. Most secondary optimizations are centered around removing
//! allocations and such, but avoiding parsing JSON is the #1 optimization.
//!
-//! When we get queries from the resolver we're given a `Dependency`. This
+//! When we get queries from the resolver we're given a [`Dependency`]. This
//! dependency in turn has a version requirement, and with lock files that
//! already exist these version requirements are exact version requirements
//! `=a.b.c`. This means that we in theory only need to parse one line of JSON
//! per query in the registry, the one that matches version `a.b.c`.
//!
//! The crates.io index, however, is not amenable to this form of query. Instead
-//! the crates.io index simply is a file where each line is a JSON blob. To
-//! learn about the versions in each JSON blob we would need to parse the JSON,
-//! defeating the purpose of trying to parse as little as possible.
+//! the crates.io index simply is a file where each line is a JSON blob, aka
+//! [`IndexPackage`]. To learn about the versions in each JSON blob we would
+//! need to parse the JSON via [`IndexSummary::parse`], defeating the purpose
+//! of trying to parse as little as possible.
//!
//! > Note that as a small aside even *loading* the JSON from the registry is
-//! > actually pretty slow. For crates.io and remote registries we don't
+//! > actually pretty slow. For crates.io and [`RemoteRegistry`] we don't
//! > actually check out the git index on disk because that takes quite some
//! > time and is quite large. Instead we use `libgit2` to read the JSON from
//! > the raw git objects. This in turn can be slow (aka show up high in
@@ -43,14 +59,14 @@
//! (first time being for an entire computer) Cargo will load the contents
//! (slowly via libgit2) from the registry. It will then (slowly) parse every
//! single line to learn about its versions. Afterwards, however, Cargo will
-//! emit a new file (a cache) which is amenable for speedily parsing in future
-//! invocations.
+//! emit a new file (a cache, representing as [`SummariesCache`]) which is
+//! amenable for speedily parsing in future invocations.
//!
//! This cache file is currently organized by basically having the semver
-//! version extracted from each JSON blob. That way Cargo can quickly and easily
-//! parse all versions contained and which JSON blob they're associated with.
-//! The JSON blob then doesn't actually need to get parsed unless the version is
-//! parsed.
+//! version extracted from each JSON blob. That way Cargo can quickly and
+//! easily parse all versions contained and which JSON blob they're associated
+//! with. The JSON blob then doesn't actually need to get parsed unless the
+//! version is parsed.
//!
//! Altogether the initial measurements of this shows a massive improvement for
//! Cargo null build performance. It's expected that the improvements earned
@@ -65,15 +81,24 @@
//! Note that this is just a high-level overview, there's of course lots of
//! details like invalidating caches and whatnot which are handled below, but
//! hopefully those are more obvious inline in the code itself.
+//!
+//! [`RemoteRegistry`]: super::remote::RemoteRegistry
+//! [`Dependency`]: crate::core::Dependency
+use crate::core::dependency::DepKind;
+use crate::core::Dependency;
use crate::core::{PackageId, SourceId, Summary};
-use crate::sources::registry::{LoadResponse, RegistryData, RegistryPackage, INDEX_V_MAX};
+use crate::sources::registry::{LoadResponse, RegistryData};
use crate::util::interning::InternedString;
+use crate::util::IntoUrl;
use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, ToSemver};
use anyhow::bail;
use cargo_util::{paths, registry::make_dep_path};
use log::{debug, info};
use semver::Version;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::collections::BTreeMap;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::io::ErrorKind;
@@ -81,19 +106,33 @@ use std::path::Path;
use std::str;
use std::task::{ready, Poll};
+/// The current version of [`SummariesCache`].
+const CURRENT_CACHE_VERSION: u8 = 3;
+
+/// The maximum schema version of the `v` field in the index this version of
+/// cargo understands. See [`IndexPackage::v`] for the detail.
+const INDEX_V_MAX: u32 = 2;
+
/// Manager for handling the on-disk index.
///
-/// Note that local and remote registries store the index differently. Local
-/// is a simple on-disk tree of files of the raw index. Remote registries are
-/// stored as a raw git repository. The different means of access are handled
-/// via the [`RegistryData`] trait abstraction.
+/// Different kinds of registries store the index differently:
///
+/// * [`LocalRegistry`]` is a simple on-disk tree of files of the raw index.
+/// * [`RemoteRegistry`] is stored as a raw git repository.
+/// * [`HttpRegistry`] fills the on-disk index cache directly without keeping
+/// any raw index.
+///
+/// These means of access are handled via the [`RegistryData`] trait abstraction.
/// This transparently handles caching of the index in a more efficient format.
+///
+/// [`LocalRegistry`]: super::local::LocalRegistry
+/// [`RemoteRegistry`]: super::remote::RemoteRegistry
+/// [`HttpRegistry`]: super::http_remote::HttpRegistry
pub struct RegistryIndex<'cfg> {
source_id: SourceId,
/// Root directory of the index for the registry.
path: Filesystem,
- /// Cache of summary data.
+ /// In-memory cache of summary data.
///
/// This is keyed off the package name. The [`Summaries`] value handles
/// loading the summary data. It keeps an optimized on-disk representation
@@ -110,14 +149,16 @@ pub struct RegistryIndex<'cfg> {
///
/// A list of summaries are loaded from disk via one of two methods:
///
-/// 1. Primarily Cargo will parse the corresponding file for a crate in the
-/// upstream crates.io registry. That's just a JSON blob per line which we
-/// can parse, extract the version, and then store here.
+/// 1. From raw registry index --- Primarily Cargo will parse the corresponding
+/// file for a crate in the upstream crates.io registry. That's just a JSON
+/// blob per line which we can parse, extract the version, and then store here.
+/// See [`IndexPackage`] and [`IndexSummary::parse`].
///
-/// 2. Alternatively, if Cargo has previously run, we'll have a cached index of
-/// dependencies for the upstream index. This is a file that Cargo maintains
-/// lazily on the local filesystem and is much faster to parse since it
-/// doesn't involve parsing all of the JSON.
+/// 2. From on-disk index cache --- If Cargo has previously run, we'll have a
+/// cached index of dependencies for the upstream index. This is a file that
+/// Cargo maintains lazily on the local filesystem and is much faster to
+/// parse since it doesn't involve parsing all of the JSON.
+/// See [`SummariesCache`].
///
/// The outward-facing interface of this doesn't matter too much where it's
/// loaded from, but it's important when reading the implementation to note that
@@ -134,37 +175,191 @@ struct Summaries {
versions: HashMap<Version, MaybeIndexSummary>,
}
-/// A lazily parsed `IndexSummary`.
+/// A lazily parsed [`IndexSummary`].
enum MaybeIndexSummary {
/// A summary which has not been parsed, The `start` and `end` are pointers
- /// into `Summaries::raw_data` which this is an entry of.
+ /// into [`Summaries::raw_data`] which this is an entry of.
Unparsed { start: usize, end: usize },
/// An actually parsed summary.
Parsed(IndexSummary),
}
-/// A parsed representation of a summary from the index.
+/// A parsed representation of a summary from the index. This is usually parsed
+/// from a line from a raw index file, or a JSON blob from on-disk index cache.
///
-/// In addition to a full `Summary` we have information on whether it is `yanked`.
+/// In addition to a full [`Summary`], we have information on whether it is `yanked`.
pub struct IndexSummary {
pub summary: Summary,
pub yanked: bool,
- /// Schema version, see [`RegistryPackage`].
+ /// Schema version, see [`IndexPackage::v`].
v: u32,
}
/// A representation of the cache on disk that Cargo maintains of summaries.
+///
/// Cargo will initially parse all summaries in the registry and will then
/// serialize that into this form and place it in a new location on disk,
/// ensuring that access in the future is much speedier.
+///
+/// For serialization and deserialization of this on-disk index cache of
+/// summaries, see [`SummariesCache::serialize`] and [`SummariesCache::parse`].
+///
+/// # The format of the index cache
+///
+/// The idea of this format is that it's a very easy file for Cargo to parse in
+/// future invocations. The read from disk should be fast and then afterwards
+/// all we need to know is what versions correspond to which JSON blob.
+///
+/// Currently the format looks like:
+///
+/// ```text
+/// +---------------+----------------------+--------------------+---+
+/// | cache version | index schema version | index file version | 0 |
+/// +---------------+----------------------+--------------------+---+
+/// ```
+///
+/// followed by one or more (version + JSON blob) pairs...
+///
+/// ```text
+/// +----------------+---+-----------+---+
+/// | semver version | 0 | JSON blob | 0 | ...
+/// +----------------+---+-----------+---+
+/// ```
+///
+/// Each field represents:
+///
+/// * _cache version_ --- Intended to ensure that there's some level of
+/// future compatibility against changes to this cache format so if different
+/// versions of Cargo share the same cache they don't get too confused.
+/// * _index schema version_ --- The schema version of the raw index file.
+/// See [`IndexPackage::v`] for the detail.
+/// * _index file version_ --- Tracks when a cache needs to be regenerated.
+/// A cache regeneration is required whenever the index file itself updates.
+/// * _semver version_ --- The version for each JSON blob. Extracted from the
+/// blob for fast queries without parsing the entire blob.
+/// * _JSON blob_ --- The actual metadata for each version of the package. It
+/// has the same representation as [`IndexPackage`].
+///
+/// # Changes between each cache version
+///
+/// * `1`: The original version.
+/// * `2`: Added the "index schema version" field so that if the index schema
+/// changes, different versions of cargo won't get confused reading each
+/// other's caches.
+/// * `3`: Bumped the version to work around an issue where multiple versions of
+/// a package were published that differ only by semver metadata. For
+/// example, openssl-src 110.0.0 and 110.0.0+1.1.0f. Previously, the cache
+/// would be incorrectly populated with two entries, both 110.0.0. After
+/// this, the metadata will be correctly included. This isn't really a format
+/// change, just a version bump to clear the incorrect cache entries. Note:
+/// the index shouldn't allow these, but unfortunately crates.io doesn't
+/// check it.
+///
+/// See [`CURRENT_CACHE_VERSION`] for the current cache version.
#[derive(Default)]
struct SummariesCache<'a> {
+ /// JSON blobs of the summaries. Each JSON blob has a [`Version`] beside,
+ /// so that Cargo can query a version without full JSON parsing.
versions: Vec<(Version, &'a [u8])>,
+ /// For cache invalidation, we tracks the index file version to determine
+ /// when to regenerate the cache itself.
index_version: &'a str,
}
+/// A single line in the index representing a single version of a package.
+#[derive(Deserialize)]
+pub struct IndexPackage<'a> {
+ /// Name of the pacakge.
+ name: InternedString,
+ /// The version of this dependency.
+ vers: Version,
+ /// All kinds of direct dependencies of the package, including dev and
+ /// build dependencies.
+ #[serde(borrow)]
+ deps: Vec<RegistryDependency<'a>>,
+ /// Set of features defined for the package, i.e., `[features]` table.
+ features: BTreeMap<InternedString, Vec<InternedString>>,
+ /// This field contains features with new, extended syntax. Specifically,
+ /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`).
+ ///
+ /// This is separated from `features` because versions older than 1.19
+ /// will fail to load due to not being able to parse the new syntax, even
+ /// with a `Cargo.lock` file.
+ features2: Option<BTreeMap<InternedString, Vec<InternedString>>>,
+ /// Checksum for verifying the integrity of the corresponding downloaded package.
+ cksum: String,
+ /// If `true`, Cargo will skip this version when resolving.
+ ///
+ /// This was added in 2014. Everything in the crates.io index has this set
+ /// now, so this probably doesn't need to be an option anymore.
+ yanked: Option<bool>,
+ /// Native library name this package links to.
+ ///
+ /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
+ /// can be `None` if published before then.
+ links: Option<InternedString>,
+ /// Required version of rust
+ ///
+ /// Corresponds to `package.rust-version`.
+ ///
+ /// Added in 2023 (see <https://github.com/rust-lang/crates.io/pull/6267>),
+ /// can be `None` if published before then or if not set in the manifest.
+ rust_version: Option<InternedString>,
+ /// The schema version for this entry.
+ ///
+ /// If this is None, it defaults to version `1`. Entries with unknown
+ /// versions are ignored.
+ ///
+ /// Version `2` schema adds the `features2` field.
+ ///
+ /// This provides a method to safely introduce changes to index entries
+ /// and allow older versions of cargo to ignore newer entries it doesn't
+ /// understand. This is honored as of 1.51, so unfortunately older
+ /// versions will ignore it, and potentially misinterpret version 2 and
+ /// newer entries.
+ ///
+ /// The intent is that versions older than 1.51 will work with a
+ /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
+ /// update` or build a lock from scratch. In that case, cargo may
+ /// incorrectly select a new package that uses a new index schema. A
+ /// workaround is to downgrade any packages that are incompatible with the
+ /// `--precise` flag of `cargo update`.
+ v: Option<u32>,
+}
+
+/// A dependency as encoded in the [`IndexPackage`] index JSON.
+#[derive(Deserialize)]
+struct RegistryDependency<'a> {
+ /// Name of the dependency. If the dependency is renamed, the original
+ /// would be stored in [`RegistryDependency::package`].
+ name: InternedString,
+ /// The SemVer requirement for this dependency.
+ #[serde(borrow)]
+ req: Cow<'a, str>,
+ /// Set of features enabled for this dependency.
+ features: Vec<InternedString>,
+ /// Whether or not this is an optional dependency.
+ optional: bool,
+ /// Whether or not default features are enabled.
+ default_features: bool,
+ /// The target platform for this dependency.
+ target: Option<Cow<'a, str>>,
+ /// The dependency kind. "dev", "build", and "normal".
+ kind: Option<Cow<'a, str>>,
+ // The URL of the index of the registry where this dependency is from.
+ // `None` if it is from the same index.
+ registry: Option<Cow<'a, str>>,
+ /// The original name if the dependency is renamed.
+ package: Option<InternedString>,
+ /// Whether or not this is a public dependency. Unstable. See [RFC 1977].
+ ///
+ /// [RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html
+ public: Option<bool>,
+}
+
impl<'cfg> RegistryIndex<'cfg> {
+ /// Creates an empty registry index at `path`.
pub fn new(
source_id: SourceId,
path: &Filesystem,
@@ -178,7 +373,9 @@ impl<'cfg> RegistryIndex<'cfg> {
}
}
- /// Returns the hash listed for a specified `PackageId`.
+ /// Returns the hash listed for a specified `PackageId`. Primarily for
+ /// 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 summary = self.summaries(&pkg.name(), &req, load)?;
@@ -191,10 +388,14 @@ impl<'cfg> RegistryIndex<'cfg> {
}
/// Load a list of summaries for `name` package in this registry which
- /// match `req`
+ /// match `req`.
+ ///
+ /// This function will semantically
+ ///
+ /// 1. parse the index file (either raw or cache),
+ /// 2. match all versions,
+ /// 3. and then return an iterator over all summaries which matched.
///
- /// This function will semantically parse the on-disk index, match all
- /// versions, and then return an iterator over all summaries which matched.
/// Internally there's quite a few layer of caching to amortize this cost
/// though since this method is called quite a lot on null builds in Cargo.
pub fn summaries<'a, 'b>(
@@ -207,12 +408,8 @@ impl<'cfg> RegistryIndex<'cfg> {
'a: 'b,
{
let source_id = self.source_id;
- let config = self.config;
- // First up actually parse what summaries we have available. If Cargo
- // has run previously this will parse a Cargo-specific cache file rather
- // than the registry itself. In effect this is intended to be a quite
- // cheap operation.
+ // First up parse what summaries we have available.
let name = InternedString::new(name);
let summaries = ready!(self.load_summaries(name, load)?);
@@ -227,15 +424,13 @@ impl<'cfg> RegistryIndex<'cfg> {
.versions
.iter_mut()
.filter_map(move |(k, v)| if req.matches(k) { Some(v) } else { None })
- .filter_map(
- move |maybe| match maybe.parse(config, raw_data, source_id) {
- Ok(summary) => Some(summary),
- Err(e) => {
- info!("failed to parse `{}` registry package: {}", name, e);
- None
- }
- },
- )
+ .filter_map(move |maybe| match maybe.parse(raw_data, source_id) {
+ Ok(summary) => Some(summary),
+ Err(e) => {
+ info!("failed to parse `{}` registry package: {}", name, e);
+ None
+ }
+ })
.filter(move |is| {
if is.v > INDEX_V_MAX {
debug!(
@@ -251,13 +446,28 @@ impl<'cfg> RegistryIndex<'cfg> {
})))
}
+ /// Actually parses what summaries we have available.
+ ///
+ /// If Cargo has run previously, this tries in this order:
+ ///
+ /// 1. Returns from in-memory cache, aka [`RegistryIndex::summaries_cache`].
+ /// 2. If missing, hands over to [`Summaries::parse`] to parse an index file.
+ ///
+ /// The actual kind index file being parsed depends on which kind of
+ /// [`RegistryData`] the `load` argument is given. For example, a
+ /// Git-based [`RemoteRegistry`] will first try a on-disk index cache
+ /// file, and then try parsing registry raw index fomr Git repository.
+ ///
+ /// In effect, this is intended to be a quite cheap operation.
+ ///
+ /// [`RemoteRegistry`]: super::remote::RemoteRegistry
fn load_summaries(
&mut self,
name: InternedString,
load: &mut dyn RegistryData,
) -> Poll<CargoResult<&mut Summaries>> {
// If we've previously loaded what versions are present for `name`, just
- // return that since our cache should still be valid.
+ // return that since our in-memory cache should still be valid.
if self.summaries_cache.contains_key(&name) {
return Poll::Ready(Ok(self.summaries_cache.get_mut(&name).unwrap()));
}
@@ -271,12 +481,7 @@ impl<'cfg> RegistryIndex<'cfg> {
// See module comment in `registry/mod.rs` for why this is structured
// the way it is.
- let fs_name = name
- .chars()
- .flat_map(|c| c.to_lowercase())
- .collect::<String>();
-
- let path = make_dep_path(&fs_name, false);
+ let path = make_dep_path(&name.to_lowercase(), false);
let summaries = ready!(Summaries::parse(
root,
&cache_root,
@@ -295,6 +500,9 @@ impl<'cfg> RegistryIndex<'cfg> {
self.summaries_cache.clear();
}
+ /// Attempts to find the packages that match a `name` and a version `req`.
+ ///
+ /// This is primarily used by [`Source::query`](super::Source).
pub fn query_inner(
&mut self,
name: &str,
@@ -324,6 +532,10 @@ impl<'cfg> RegistryIndex<'cfg> {
.map_ok(|_| ())
}
+ /// Inner implementation of [`Self::query_inner`]. Returns the number of
+ /// summaries we've got.
+ ///
+ /// The `online` controls whether Cargo can access the network when needed.
fn query_inner_with_online(
&mut self,
name: &str,
@@ -404,6 +616,7 @@ impl<'cfg> RegistryIndex<'cfg> {
Poll::Ready(Ok(count))
}
+ /// Looks into the summaries to check if a package has been yanked.
pub fn is_yanked(
&mut self,
pkg: PackageId,
@@ -418,23 +631,26 @@ impl<'cfg> RegistryIndex<'cfg> {
}
impl Summaries {
- /// Parse out a `Summaries` instances from on-disk state.
+ /// Parse out a [`Summaries`] instances from on-disk state.
///
- /// This will attempt to prefer parsing a previous cache file that already
- /// exists from a previous invocation of Cargo (aka you're typing `cargo
- /// build` again after typing it previously). If parsing fails or the cache
- /// isn't found, then we take a slower path which loads the full descriptor
- /// for `relative` from the underlying index (aka typically libgit2 with
- /// crates.io) and then parse everything in there.
+ /// This will do the followings in order:
///
- /// * `root` - this is the root argument passed to `load`
- /// * `cache_root` - this is the root on the filesystem itself of where to
- /// store cache files.
- /// * `relative` - this is the file we're loading from cache or the index
+ /// 1. Attempt to prefer parsing a previous index cache file that already
+ /// exists from a previous invocation of Cargo (aka you're typing `cargo
+ /// build` again after typing it previously).
+ /// 2. If parsing fails, or the cache isn't found or is invalid, we then
+ /// take a slower path which loads the full descriptor for `relative`
+ /// from the underlying index (aka libgit2 with crates.io, or from a
+ /// remote HTTP index) and then parse everything in there.
+ ///
+ /// * `root` --- this is the root argument passed to `load`
+ /// * `cache_root` --- this is the root on the filesystem itself of where
+ /// to store cache files.
+ /// * `relative` --- this is the file we're loading from cache or the index
/// data
- /// * `source_id` - the registry's SourceId used when parsing JSON blobs to
- /// create summaries.
- /// * `load` - the actual index implementation which may be very slow to
+ /// * `source_id` --- the registry's SourceId used when parsing JSON blobs
+ /// to create summaries.
+ /// * `load` --- the actual index implementation which may be very slow to
/// call. We avoid this if we can.
pub fn parse(
root: &Path,
@@ -495,7 +711,7 @@ impl Summaries {
// allow future cargo implementations to break the
// interpretation of each line here and older cargo will simply
// ignore the new lines.
- let summary = match IndexSummary::parse(config, line, source_id) {
+ let summary = match IndexSummary::parse(line, source_id) {
Ok(summary) => summary,
Err(e) => {
// This should only happen when there is an index
@@ -549,8 +765,8 @@ impl Summaries {
}
}
- /// Parses an open `File` which represents information previously cached by
- /// Cargo.
+ /// Parses the contents of an on-disk cache, aka [`SummariesCache`], which
+ /// represents information previously cached by Cargo.
pub fn parse_cache(contents: Vec<u8>) -> CargoResult<(Summaries, InternedString)> {
let cache = SummariesCache::parse(&contents)?;
let index_version = InternedString::new(cache.index_version);
@@ -577,46 +793,8 @@ impl Summaries {
}
}
-// Implementation of serializing/deserializing the cache of summaries on disk.
-// Currently the format looks like:
-//
-// +--------------------+----------------------+-------------+---+
-// | cache version byte | index format version | git sha rev | 0 |
-// +--------------------+----------------------+-------------+---+
-//
-// followed by...
-//
-// +----------------+---+------------+---+
-// | semver version | 0 | JSON blob | 0 | ...
-// +----------------+---+------------+---+
-//
-// The idea is that this is a very easy file for Cargo to parse in future
-// invocations. The read from disk should be quite fast and then afterwards all
-// we need to know is what versions correspond to which JSON blob.
-//
-// The leading version byte is intended to ensure that there's some level of
-// future compatibility against changes to this cache format so if different
-// versions of Cargo share the same cache they don't get too confused. The git
-// sha lets us know when the file needs to be regenerated (it needs regeneration
-// whenever the index itself updates).
-//
-// Cache versions:
-// * `1`: The original version.
-// * `2`: Added the "index format version" field so that if the index format
-// changes, different versions of cargo won't get confused reading each
-// other's caches.
-// * `3`: Bumped the version to work around an issue where multiple versions of
-// a package were published that differ only by semver metadata. For
-// example, openssl-src 110.0.0 and 110.0.0+1.1.0f. Previously, the cache
-// would be incorrectly populated with two entries, both 110.0.0. After
-// this, the metadata will be correctly included. This isn't really a format
-// change, just a version bump to clear the incorrect cache entries. Note:
-// the index shouldn't allow these, but unfortunately crates.io doesn't
-// check it.
-
-const CURRENT_CACHE_VERSION: u8 = 3;
-
impl<'a> SummariesCache<'a> {
+ /// Deserializes an on-disk cache.
fn parse(data: &'a [u8]) -> CargoResult<SummariesCache<'a>> {
// NB: keep this method in sync with `serialize` below
let (first_byte, rest) = data
@@ -627,13 +805,11 @@ impl<'a> SummariesCache<'a> {
}
let index_v_bytes = rest
.get(..4)
- .ok_or_else(|| anyhow::anyhow!("cache expected 4 bytes for index version"))?;
+ .ok_or_else(|| anyhow::anyhow!("cache expected 4 bytes for index schema version"))?;
let index_v = u32::from_le_bytes(index_v_bytes.try_into().unwrap());
if index_v != INDEX_V_MAX {
bail!(
- "index format version {} doesn't match the version I know ({})",
- index_v,
- INDEX_V_MAX
+ "index schema version {index_v} doesn't match the version I know ({INDEX_V_MAX})",
);
}
let rest = &rest[4..];
@@ -655,6 +831,7 @@ impl<'a> SummariesCache<'a> {
Ok(ret)
}
+ /// Serializes itself with a given `index_version`.
fn serialize(&self, index_version: &str) -> Vec<u8> {
// NB: keep this method in sync with `parse` above
let size = self
@@ -683,17 +860,12 @@ impl MaybeIndexSummary {
/// Does nothing if this is already `Parsed`, and otherwise the `raw_data`
/// passed in is sliced with the bounds in `Unparsed` and then actually
/// parsed.
- fn parse(
- &mut self,
- config: &Config,
- raw_data: &[u8],
- source_id: SourceId,
- ) -> CargoResult<&IndexSummary> {
+ fn parse(&mut self, raw_data: &[u8], source_id: SourceId) -> CargoResult<&IndexSummary> {
let (start, end) = match self {
MaybeIndexSummary::Unparsed { start, end } => (*start, *end),
MaybeIndexSummary::Parsed(summary) => return Ok(summary),
};
- let summary = IndexSummary::parse(config, &raw_data[start..end], source_id)?;
+ let summary = IndexSummary::parse(&raw_data[start..end], source_id)?;
*self = MaybeIndexSummary::Parsed(summary);
match self {
MaybeIndexSummary::Unparsed { .. } => unreachable!(),
@@ -709,18 +881,19 @@ impl From<IndexSummary> for MaybeIndexSummary {
}
impl IndexSummary {
- /// Parses a line from the registry's index file into an `IndexSummary` for
- /// a package.
+ /// Parses a line from the registry's index file into an [`IndexSummary`]
+ /// for a package.
///
- /// The `line` provided is expected to be valid JSON.
- fn parse(config: &Config, line: &[u8], source_id: SourceId) -> CargoResult<IndexSummary> {
+ /// The `line` provided is expected to be valid JSON. It is supposed to be
+ /// a [`IndexPackage`].
+ fn parse(line: &[u8], source_id: SourceId) -> CargoResult<IndexSummary> {
// ****CAUTION**** Please be extremely careful with returning errors
// from this function. Entries that error are not included in the
// index cache, and can cause cargo to get confused when switching
// between different versions that understand the index differently.
// Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION
// values carefully when making changes here.
- let RegistryPackage {
+ let IndexPackage {
name,
vers,
cksum,
@@ -744,7 +917,7 @@ impl IndexSummary {
features.entry(name).or_default().extend(values);
}
}
- let mut summary = Summary::new(config, pkgid, deps, &features, links, rust_version)?;
+ let mut summary = Summary::new(pkgid, deps, &features, links, rust_version)?;
summary.set_checksum(cksum);
Ok(IndexSummary {
summary,
@@ -754,6 +927,71 @@ impl IndexSummary {
}
}
+impl<'a> RegistryDependency<'a> {
+ /// Converts an encoded dependency in the registry to a cargo dependency
+ pub fn into_dep(self, default: SourceId) -> CargoResult<Dependency> {
+ let RegistryDependency {
+ name,
+ req,
+ mut features,
+ optional,
+ default_features,
+ target,
+ kind,
+ registry,
+ package,
+ public,
+ } = self;
+
+ let id = if let Some(registry) = &registry {
+ SourceId::for_registry(&registry.into_url()?)?
+ } else {
+ default
+ };
+
+ let mut dep = Dependency::parse(package.unwrap_or(name), Some(&req), id)?;
+ if package.is_some() {
+ dep.set_explicit_name_in_toml(name);
+ }
+ let kind = match kind.as_deref().unwrap_or("") {
+ "dev" => DepKind::Development,
+ "build" => DepKind::Build,
+ _ => DepKind::Normal,
+ };
+
+ let platform = match target {
+ Some(target) => Some(target.parse()?),
+ None => None,
+ };
+
+ // All dependencies are private by default
+ let public = public.unwrap_or(false);
+
+ // Unfortunately older versions of cargo and/or the registry ended up
+ // publishing lots of entries where the features array contained the
+ // empty feature, "", inside. This confuses the resolution process much
+ // later on and these features aren't actually valid, so filter them all
+ // out here.
+ features.retain(|s| !s.is_empty());
+
+ // In index, "registry" is null if it is from the same index.
+ // In Cargo.toml, "registry" is None if it is from the default
+ if !id.is_crates_io() {
+ dep.set_registry_id(id);
+ }
+
+ dep.set_optional(optional)
+ .set_default_features(default_features)
+ .set_features(features)
+ .set_platform(platform)
+ .set_kind(kind)
+ .set_public(public);
+
+ Ok(dep)
+ }
+}
+
+/// Like [`slice::split`] but is optimized by [`memchr`].
fn split(haystack: &[u8], needle: u8) -> impl Iterator<Item = &[u8]> {
struct Split<'a> {
haystack: &'a [u8],
@@ -778,3 +1016,36 @@ fn split(haystack: &[u8], needle: u8) -> impl Iterator<Item = &[u8]> {
Split { haystack, needle }
}
+
+#[test]
+fn escaped_char_in_index_json_blob() {
+ let _: IndexPackage<'_> = serde_json::from_str(
+ r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#,
+ )
+ .unwrap();
+ let _: IndexPackage<'_> = serde_json::from_str(
+ r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"#
+ ).unwrap();
+
+ // Now we add escaped cher all the places they can go
+ // these are not valid, but it should error later than json parsing
+ let _: IndexPackage<'_> = serde_json::from_str(
+ r#"{
+ "name":"This name has a escaped cher in it \n\t\" ",
+ "vers":"0.0.1",
+ "deps":[{
+ "name": " \n\t\" ",
+ "req": " \n\t\" ",
+ "features": [" \n\t\" "],
+ "optional": true,
+ "default_features": true,
+ "target": " \n\t\" ",
+ "kind": " \n\t\" ",
+ "registry": " \n\t\" "
+ }],
+ "cksum":"bae3",
+ "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]},
+ "links":" \n\t\" "}"#,
+ )
+ .unwrap();
+}
diff --git a/src/tools/cargo/src/cargo/sources/registry/local.rs b/src/tools/cargo/src/cargo/sources/registry/local.rs
index 89419191f..68fc61a6c 100644
--- a/src/tools/cargo/src/cargo/sources/registry/local.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/local.rs
@@ -1,3 +1,5 @@
+//! Access to a regstiry on the local filesystem. See [`LocalRegistry`] for more.
+
use crate::core::PackageId;
use crate::sources::registry::{LoadResponse, MaybeLock, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
@@ -10,18 +12,68 @@ use std::path::Path;
use std::task::Poll;
/// A local registry is a registry that lives on the filesystem as a set of
-/// `.crate` files with an `index` directory in the same format as a remote
+/// `.crate` files with an `index` directory in the [same format] as a remote
/// registry.
+///
+/// This type is primarily accessed through the [`RegistryData`] trait.
+///
+/// When a local registry is requested for a package, it simply looks into what
+/// its index has under the `index` directory. When [`LocalRegistry::download`]
+/// is called, a local registry verifies the checksum of the requested `.crate`
+/// tarball and then unpacks it to `$CARGO_HOME/.registry/src`.
+///
+/// > Note that there is a third-party subcommand [`cargo-local-registry`],
+/// > which happened to be developed by a former Cargo team member when local
+/// > registry was introduced. The tool is to ease the burden of maintaining
+/// > local registries. However, in general the Cargo team avoids recommending
+/// > any specific third-party crate. Just FYI.
+///
+/// [same format]: super#the-format-of-the-index
+/// [`cargo-local-registry`]: https://crates.io/crates/cargo-local-registry
+///
+/// # Filesystem hierarchy
+///
+/// Here is an example layout of a local registry on a local filesystem:
+///
+/// ```text
+/// [registry root]/
+/// ├── index/ # registry index
+/// │ ├── an/
+/// │ │ └── yh/
+/// │ │ └── anyhow
+/// │ ├── ru/
+/// │ │ └── st/
+/// │ │ ├── rustls
+/// │ │ └── rustls-ffi
+/// │ └── se/
+/// │ └── mv/
+/// │ └── semver
+/// ├── anyhow-1.0.71.crate # pre-downloaded crate tarballs
+/// ├── rustls-0.20.8.crate
+/// ├── rustls-ffi-0.8.2.crate
+/// └── semver-1.0.17.crate
+/// ```
+///
+/// For general concepts of registries, see the [module-level documentation](crate::sources::registry).
pub struct LocalRegistry<'cfg> {
+ /// Path to the registry index.
index_path: Filesystem,
+ /// Root path of this local registry.
root: Filesystem,
+ /// Path where this local registry extract `.crate` tarballs to.
src_path: Filesystem,
config: &'cfg Config,
+ /// Whether this source has updated all package informations it may contain.
updated: bool,
+ /// Disables status messages.
quiet: bool,
}
impl<'cfg> LocalRegistry<'cfg> {
+ /// Creates a local registry at `root`.
+ ///
+ /// * `name` --- Name of a path segment where `.crate` tarballs are stored.
+ /// Expect to be unique.
pub fn new(root: &Path, config: &'cfg Config, name: &str) -> LocalRegistry<'cfg> {
LocalRegistry {
src_path: config.registry_source_path().join(name),
@@ -115,17 +167,15 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> {
}
fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
- let crate_file = format!("{}-{}.crate", pkg.name(), pkg.version());
-
// Note that the usage of `into_path_unlocked` here is because the local
// crate files here never change in that we're not the one writing them,
// so it's not our responsibility to synchronize access to them.
- let path = self.root.join(&crate_file).into_path_unlocked();
+ let path = self.root.join(&pkg.tarball_name()).into_path_unlocked();
let mut crate_file = paths::open(&path)?;
// If we've already got an unpacked version of this crate, then skip the
// checksum below as it is in theory already verified.
- let dst = format!("{}-{}", pkg.name(), pkg.version());
+ let dst = path.file_stem().unwrap();
if self.src_path.join(dst).into_path_unlocked().exists() {
return Ok(MaybeLock::Ready(crate_file));
}
diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs
index c76ee6142..a0178db55 100644
--- a/src/tools/cargo/src/cargo/sources/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs
@@ -2,13 +2,47 @@
//!
//! # What's a Registry?
//!
-//! Registries are central locations where packages can be uploaded to,
+//! [Registries] are central locations where packages can be uploaded to,
//! discovered, and searched for. The purpose of a registry is to have a
//! location that serves as permanent storage for versions of a crate over time.
//!
-//! Compared to git sources, a registry provides many packages as well as many
-//! versions simultaneously. Git sources can also have commits deleted through
-//! rebasings where registries cannot have their versions deleted.
+//! Compared to git sources (see [`GitSource`]), a registry provides many
+//! packages as well as many versions simultaneously. Git sources can also
+//! have commits deleted through rebasings where registries cannot have their
+//! versions deleted.
+//!
+//! In Cargo, [`RegistryData`] is an abstraction over each kind of actual
+//! registry, and [`RegistrySource`] connects those implementations to
+//! [`Source`] trait. Two prominent features these abstractions provide are
+//!
+//! * A way to query the metadata of a package from a registry. The metadata
+//! comes from the index.
+//! * A way to download package contents (a.k.a source files) that are required
+//! when building the package itself.
+//!
+//! We'll cover each functionality later.
+//!
+//! [Registries]: https://doc.rust-lang.org/nightly/cargo/reference/registries.html
+//! [`GitSource`]: super::GitSource
+//!
+//! # Different Kinds of Registries
+//!
+//! Cargo provides multiple kinds of registries. Each of them serves the index
+//! and package contents in a slightly different way. Namely,
+//!
+//! * [`LocalRegistry`] --- Serves the index and package contents entirely on
+//! a local filesystem.
+//! * [`RemoteRegistry`] --- Serves the index ahead of time from a Git
+//! repository, and package contents are downloaded as needed.
+//! * [`HttpRegistry`] --- Serves both the index and package contents on demand
+//! over a HTTP-based registry API. This is the default starting from 1.70.0.
+//!
+//! Each registry has its own [`RegistryData`] implementation, and can be
+//! created from either [`RegistrySource::local`] or [`RegistrySource::remote`].
+//!
+//! [`LocalRegistry`]: local::LocalRegistry
+//! [`RemoteRegistry`]: remote::RemoteRegistry
+//! [`HttpRegistry`]: http_remote::HttpRegistry
//!
//! # The Index of a Registry
//!
@@ -20,36 +54,16 @@
//! available on a registry, what versions are available, and what the
//! dependencies for each version is.
//!
-//! One method of doing so would be having the registry expose an HTTP endpoint
-//! which can be queried with a list of packages and a response of their
-//! dependencies and versions is returned. This is somewhat inefficient however
-//! as we may have to hit the endpoint many times and we may have already
-//! queried for much of the data locally already (for other packages, for
-//! example). This also involves inventing a transport format between the
-//! registry and Cargo itself, so this route was not taken.
-//!
-//! Instead, Cargo communicates with registries through a git repository
-//! referred to as the Index. The Index of a registry is essentially an easily
-//! query-able version of the registry's database for a list of versions of a
-//! package as well as a list of dependencies for each version.
+//! To solve the problem, a registry must provide an index of package metadata.
+//! The index of a registry is essentially an easily query-able version of the
+//! registry's database for a list of versions of a package as well as a list
+//! of dependencies for each version. The exact format of the index is
+//! described later.
//!
-//! Using git to host this index provides a number of benefits:
+//! See the [`index`] module for topics about the management, parsing, caching,
+//! and versioning for the on-disk index.
//!
-//! * The entire index can be stored efficiently locally on disk. This means
-//! that all queries of a registry can happen locally and don't need to touch
-//! the network.
-//!
-//! * Updates of the index are quite efficient. Using git buys incremental
-//! updates, compressed transmission, etc for free. The index must be updated
-//! each time we need fresh information from a registry, but this is one
-//! update of a git repository that probably hasn't changed a whole lot so
-//! it shouldn't be too expensive.
-//!
-//! Additionally, each modification to the index is just appending a line at
-//! the end of a file (the exact format is described later). This means that
-//! the commits for an index are quite small and easily applied/compressible.
-//!
-//! ## The format of the Index
+//! ## The Format of The Index
//!
//! The index is a store for the list of versions for all packages known, so its
//! format on disk is optimized slightly to ensure that `ls registry` doesn't
@@ -59,9 +73,12 @@
//! about the format of the registry:
//!
//! 1. Each crate will have one file corresponding to it. Each version for a
-//! crate will just be a line in this file.
+//! crate will just be a line in this file (see [`IndexPackage`] for its
+//! representation).
//! 2. There will be two tiers of directories for crate names, under which
//! crates corresponding to those tiers will be located.
+//! (See [`cargo_util::registry::make_dep_path`] for the implementation of
+//! this layout hierarchy.)
//!
//! As an example, this is an example hierarchy of an index:
//!
@@ -99,26 +116,30 @@
//! The purpose of this layout is to hopefully cut down on `ls` sizes as well as
//! efficient lookup based on the crate name itself.
//!
-//! ## Crate files
+//! See [The Cargo Book: Registry Index][registry-index] for the public
+//! interface on the index format.
+//!
+//! [registry-index]: https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html
+//!
+//! ## The Index Files
//!
//! Each file in the index is the history of one crate over time. Each line in
//! the file corresponds to one version of a crate, stored in JSON format (see
-//! the `RegistryPackage` structure below).
+//! the [`IndexPackage`] structure).
//!
-//! As new versions are published, new lines are appended to this file. The only
-//! modifications to this file that should happen over time are yanks of a
-//! particular version.
+//! As new versions are published, new lines are appended to this file. **The
+//! only modifications to this file that should happen over time are yanks of a
+//! particular version.**
//!
//! # Downloading Packages
//!
-//! The purpose of the Index was to provide an efficient method to resolve the
-//! dependency graph for a package. So far we only required one network
-//! interaction to update the registry's repository (yay!). After resolution has
-//! been performed, however we need to download the contents of packages so we
-//! can read the full manifest and build the source code.
+//! The purpose of the index was to provide an efficient method to resolve the
+//! dependency graph for a package. After resolution has been performed, we need
+//! to download the contents of packages so we can read the full manifest and
+//! build the source code.
//!
-//! To accomplish this, this source's `download` method will make an HTTP
-//! request per-package requested to download tarballs into a local cache. These
+//! To accomplish this, [`RegistryData::download`] will "make" an HTTP request
+//! per-package requested to download tarballs into a local cache. These
//! tarballs will then be unpacked into a destination folder.
//!
//! Note that because versions uploaded to the registry are frozen forever that
@@ -128,7 +149,8 @@
//!
//! # Filesystem Hierarchy
//!
-//! Overall, the `$HOME/.cargo` looks like this when talking about the registry:
+//! Overall, the `$HOME/.cargo` looks like this when talking about the registry
+//! (remote registries, specifically):
//!
//! ```notrust
//! # A folder under which all registry metadata is hosted (similar to
@@ -144,8 +166,8 @@
//! registry2-<hash>/
//! ...
//!
-//! # This folder is a cache for all downloaded tarballs from a registry.
-//! # Once downloaded and verified, a tarball never changes.
+//! # This folder is a cache for all downloaded tarballs (`.crate` file)
+//! # from a registry. Once downloaded and verified, a tarball never changes.
//! cache/
//! registry1-<hash>/<pkg>-<version>.crate
//! ...
@@ -153,13 +175,14 @@
//! # Location in which all tarballs are unpacked. Each tarball is known to
//! # be frozen after downloading, so transitively this folder is also
//! # frozen once its unpacked (it's never unpacked again)
+//! # CAVEAT: They are not read-only. See rust-lang/cargo#9455.
//! src/
//! registry1-<hash>/<pkg>-<version>/...
//! ...
//! ```
+//!
+//! [`IndexPackage`]: index::IndexPackage
-use std::borrow::Cow;
-use std::collections::BTreeMap;
use std::collections::HashSet;
use std::fs;
use std::fs::{File, OpenOptions};
@@ -173,35 +196,30 @@ use anyhow::Context as _;
use cargo_util::paths::{self, exclude_from_backups_and_indexing};
use flate2::read::GzDecoder;
use log::debug;
-use semver::Version;
use serde::Deserialize;
use serde::Serialize;
use tar::Archive;
-use crate::core::dependency::{DepKind, Dependency};
+use crate::core::dependency::Dependency;
use crate::core::source::MaybePackage;
use crate::core::{Package, PackageId, QueryKind, Source, SourceId, Summary};
use crate::sources::PathSource;
use crate::util::hex;
-use crate::util::interning::InternedString;
-use crate::util::into_url::IntoUrl;
use crate::util::network::PollExt;
use crate::util::{
restricted_names, CargoResult, Config, Filesystem, LimitErrorReader, OptVersionReq,
};
+/// The `.cargo-ok` file is used to track if the source is already unpacked.
+/// See [`RegistrySource::unpack_package`] for more.
+///
+/// Not to be confused with `.cargo-ok` file in git sources.
const PACKAGE_SOURCE_LOCK: &str = ".cargo-ok";
+
pub const CRATES_IO_INDEX: &str = "https://github.com/rust-lang/crates.io-index";
pub const CRATES_IO_HTTP_INDEX: &str = "sparse+https://index.crates.io/";
pub const CRATES_IO_REGISTRY: &str = "crates-io";
pub const CRATES_IO_DOMAIN: &str = "crates.io";
-const CRATE_TEMPLATE: &str = "{crate}";
-const VERSION_TEMPLATE: &str = "{version}";
-const PREFIX_TEMPLATE: &str = "{prefix}";
-const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
-const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
-const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024;
-const MAX_COMPRESSION_RATIO: usize = 20; // 20:1
/// The content inside `.cargo-ok`.
/// See [`RegistrySource::unpack_package`] for more.
@@ -211,13 +229,15 @@ struct LockMetadata {
v: u32,
}
-/// A "source" for a local (see `local::LocalRegistry`) or remote (see
-/// `remote::RemoteRegistry`) registry.
+/// A [`Source`] implementation for a local or a remote registry.
///
-/// This contains common functionality that is shared between the two registry
-/// kinds, with the registry-specific logic implemented as part of the
+/// This contains common functionality that is shared between each registry
+/// kind, with the registry-specific logic implemented as part of the
/// [`RegistryData`] trait referenced via the `ops` field.
+///
+/// For general concepts of registries, see the [module-level documentation](crate::sources::registry).
pub struct RegistrySource<'cfg> {
+ /// The unique identifier of this source.
source_id: SourceId,
/// The path where crate files are extracted (`$CARGO_HOME/registry/src/$REG-HASH`).
src_path: Filesystem,
@@ -237,7 +257,19 @@ pub struct RegistrySource<'cfg> {
yanked_whitelist: HashSet<PackageId>,
}
-/// The `config.json` file stored in the index.
+/// The [`config.json`] file stored in the index.
+///
+/// The config file may look like:
+///
+/// ```json
+/// {
+/// "dl": "https://example.com/api/{crate}/{version}/download",
+/// "api": "https://example.com/api",
+/// "auth-required": false # unstable feature (RFC 3139)
+/// }
+/// ```
+///
+/// [`config.json`]: https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html#index-configuration
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct RegistryConfig {
@@ -257,6 +289,9 @@ pub struct RegistryConfig {
/// will be extended with `/{crate}/{version}/download` to
/// support registries like crates.io which were created before the
/// templating setup was created.
+ ///
+ /// For more on the template of the download URL, see [Index Configuration](
+ /// https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html#index-configuration).
pub dl: String,
/// API endpoint for the registry. This is what's actually hit to perform
@@ -264,183 +299,13 @@ pub struct RegistryConfig {
/// If this is None, the registry does not support API commands.
pub api: Option<String>,
- /// Whether all operations require authentication.
+ /// Whether all operations require authentication. See [RFC 3139].
+ ///
+ /// [RFC 3139]: https://rust-lang.github.io/rfcs/3139-cargo-alternative-registry-auth.html
#[serde(default)]
pub auth_required: bool,
}
-/// The maximum version of the `v` field in the index this version of cargo
-/// understands.
-pub(crate) const INDEX_V_MAX: u32 = 2;
-
-/// A single line in the index representing a single version of a package.
-#[derive(Deserialize)]
-pub struct RegistryPackage<'a> {
- name: InternedString,
- vers: Version,
- #[serde(borrow)]
- deps: Vec<RegistryDependency<'a>>,
- features: BTreeMap<InternedString, Vec<InternedString>>,
- /// This field contains features with new, extended syntax. Specifically,
- /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`).
- ///
- /// This is separated from `features` because versions older than 1.19
- /// will fail to load due to not being able to parse the new syntax, even
- /// with a `Cargo.lock` file.
- features2: Option<BTreeMap<InternedString, Vec<InternedString>>>,
- cksum: String,
- /// If `true`, Cargo will skip this version when resolving.
- ///
- /// This was added in 2014. Everything in the crates.io index has this set
- /// now, so this probably doesn't need to be an option anymore.
- yanked: Option<bool>,
- /// Native library name this package links to.
- ///
- /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
- /// can be `None` if published before then.
- links: Option<InternedString>,
- /// Required version of rust
- ///
- /// Corresponds to `package.rust-version`.
- ///
- /// Added in 2023 (see <https://github.com/rust-lang/crates.io/pull/6267>),
- /// can be `None` if published before then or if not set in the manifest.
- rust_version: Option<InternedString>,
- /// The schema version for this entry.
- ///
- /// If this is None, it defaults to version 1. Entries with unknown
- /// versions are ignored.
- ///
- /// Version `2` format adds the `features2` field.
- ///
- /// This provides a method to safely introduce changes to index entries
- /// and allow older versions of cargo to ignore newer entries it doesn't
- /// understand. This is honored as of 1.51, so unfortunately older
- /// versions will ignore it, and potentially misinterpret version 2 and
- /// newer entries.
- ///
- /// The intent is that versions older than 1.51 will work with a
- /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
- /// update` or build a lock from scratch. In that case, cargo may
- /// incorrectly select a new package that uses a new index format. A
- /// workaround is to downgrade any packages that are incompatible with the
- /// `--precise` flag of `cargo update`.
- v: Option<u32>,
-}
-
-#[test]
-fn escaped_char_in_json() {
- let _: RegistryPackage<'_> = serde_json::from_str(
- r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#,
- )
- .unwrap();
- let _: RegistryPackage<'_> = serde_json::from_str(
- r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"#
- ).unwrap();
-
- // Now we add escaped cher all the places they can go
- // these are not valid, but it should error later than json parsing
- let _: RegistryPackage<'_> = serde_json::from_str(
- r#"{
- "name":"This name has a escaped cher in it \n\t\" ",
- "vers":"0.0.1",
- "deps":[{
- "name": " \n\t\" ",
- "req": " \n\t\" ",
- "features": [" \n\t\" "],
- "optional": true,
- "default_features": true,
- "target": " \n\t\" ",
- "kind": " \n\t\" ",
- "registry": " \n\t\" "
- }],
- "cksum":"bae3",
- "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]},
- "links":" \n\t\" "}"#,
- )
- .unwrap();
-}
-
-/// A dependency as encoded in the index JSON.
-#[derive(Deserialize)]
-struct RegistryDependency<'a> {
- name: InternedString,
- #[serde(borrow)]
- req: Cow<'a, str>,
- features: Vec<InternedString>,
- optional: bool,
- default_features: bool,
- target: Option<Cow<'a, str>>,
- kind: Option<Cow<'a, str>>,
- registry: Option<Cow<'a, str>>,
- package: Option<InternedString>,
- public: Option<bool>,
-}
-
-impl<'a> RegistryDependency<'a> {
- /// Converts an encoded dependency in the registry to a cargo dependency
- pub fn into_dep(self, default: SourceId) -> CargoResult<Dependency> {
- let RegistryDependency {
- name,
- req,
- mut features,
- optional,
- default_features,
- target,
- kind,
- registry,
- package,
- public,
- } = self;
-
- let id = if let Some(registry) = &registry {
- SourceId::for_registry(&registry.into_url()?)?
- } else {
- default
- };
-
- let mut dep = Dependency::parse(package.unwrap_or(name), Some(&req), id)?;
- if package.is_some() {
- dep.set_explicit_name_in_toml(name);
- }
- let kind = match kind.as_deref().unwrap_or("") {
- "dev" => DepKind::Development,
- "build" => DepKind::Build,
- _ => DepKind::Normal,
- };
-
- let platform = match target {
- Some(target) => Some(target.parse()?),
- None => None,
- };
-
- // All dependencies are private by default
- let public = public.unwrap_or(false);
-
- // Unfortunately older versions of cargo and/or the registry ended up
- // publishing lots of entries where the features array contained the
- // empty feature, "", inside. This confuses the resolution process much
- // later on and these features aren't actually valid, so filter them all
- // out here.
- features.retain(|s| !s.is_empty());
-
- // In index, "registry" is null if it is from the same index.
- // In Cargo.toml, "registry" is None if it is from the default
- if !id.is_crates_io() {
- dep.set_registry_id(id);
- }
-
- dep.set_optional(optional)
- .set_default_features(default_features)
- .set_features(features)
- .set_platform(platform)
- .set_kind(kind)
- .set_public(public);
-
- Ok(dep)
- }
-}
-
/// Result from loading data from a registry.
pub enum LoadResponse {
/// The cache is valid. The cached data should be used.
@@ -449,6 +314,7 @@ pub enum LoadResponse {
/// The cache is out of date. Returned data should be used.
Data {
raw_data: Vec<u8>,
+ /// Version of this data to determine whether it is out of date.
index_version: Option<String>,
},
@@ -456,10 +322,11 @@ pub enum LoadResponse {
NotFound,
}
-/// An abstract interface to handle both a local (see `local::LocalRegistry`)
-/// and remote (see `remote::RemoteRegistry`) registry.
+/// An abstract interface to handle both a local and and remote registry.
///
-/// This allows [`RegistrySource`] to abstractly handle both registry kinds.
+/// This allows [`RegistrySource`] to abstractly handle each registry kind.
+///
+/// For general concepts of registries, see the [module-level documentation](crate::sources::registry).
pub trait RegistryData {
/// Performs initialization for the registry.
///
@@ -470,14 +337,15 @@ pub trait RegistryData {
/// Returns the path to the index.
///
/// Note that different registries store the index in different formats
- /// (remote=git, local=files).
+ /// (remote = git, http & local = files).
fn index_path(&self) -> &Filesystem;
/// Loads the JSON for a specific named package from the index.
///
/// * `root` is the root path to the index.
/// * `path` is the relative path to the package to load (like `ca/rg/cargo`).
- /// * `index_version` is the version of the requested crate data currently in cache.
+ /// * `index_version` is the version of the requested crate data currently
+ /// in cache. This is useful for checking if a local cache is outdated.
fn load(
&mut self,
root: &Path,
@@ -568,6 +436,8 @@ mod index;
mod local;
mod remote;
+/// Generates a unique name for [`SourceId`] to have a unique path to put their
+/// index files.
fn short_name(id: SourceId, is_shallow: bool) -> String {
let hash = hex::short_hash(&id);
let ident = id.url().host_str().unwrap_or("").to_string();
@@ -579,6 +449,11 @@ fn short_name(id: SourceId, is_shallow: bool) -> String {
}
impl<'cfg> RegistrySource<'cfg> {
+ /// Creates a [`Source`] of a "remote" registry.
+ /// It could be either an HTTP-based [`http_remote::HttpRegistry`] or
+ /// a Git-based [`remote::RemoteRegistry`].
+ ///
+ /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked.
pub fn remote(
source_id: SourceId,
yanked_whitelist: &HashSet<PackageId>,
@@ -608,6 +483,10 @@ impl<'cfg> RegistrySource<'cfg> {
))
}
+ /// Creates a [`Source`] of a local registry, with [`local::LocalRegistry`] under the hood.
+ ///
+ /// * `path` --- The root path of a local registry on the file system.
+ /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked.
pub fn local(
source_id: SourceId,
path: &Path,
@@ -619,6 +498,12 @@ impl<'cfg> RegistrySource<'cfg> {
RegistrySource::new(source_id, config, &name, Box::new(ops), yanked_whitelist)
}
+ /// Creates a source of a registry. This is a inner helper function.
+ ///
+ /// * `name` --- Name of a path segment which may affect where `.crate`
+ /// tarballs, the registry index and cache are stored. Expect to be unique.
+ /// * `ops` --- The underlying [`RegistryData`] type.
+ /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked.
fn new(
source_id: SourceId,
config: &'cfg Config,
@@ -636,7 +521,7 @@ impl<'cfg> RegistrySource<'cfg> {
}
}
- /// Decode the configuration stored within the registry.
+ /// Decode the [configuration](RegistryConfig) stored within the registry.
///
/// This requires that the index has been at least checked out.
pub fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
@@ -692,8 +577,6 @@ impl<'cfg> RegistrySource<'cfg> {
///
/// [CVE-2022-36113]: https://blog.rust-lang.org/2022/09/14/cargo-cves.html#arbitrary-file-corruption-cve-2022-36113
fn unpack_package(&self, pkg: PackageId, tarball: &File) -> CargoResult<PathBuf> {
- // The `.cargo-ok` file is used to track if the source is already
- // unpacked.
let package_dir = format!("{}-{}", pkg.name(), pkg.version());
let dst = self.src_path.join(&package_dir);
let path = dst.join(PACKAGE_SOURCE_LOCK);
@@ -786,6 +669,12 @@ impl<'cfg> RegistrySource<'cfg> {
Ok(unpack_dir.to_path_buf())
}
+ /// Turns the downloaded `.crate` tarball file into a [`Package`].
+ ///
+ /// This unconditionally sets checksum for the returned package, so it
+ /// should only be called after doing integrity check. That is to say,
+ /// you need to call either [`RegistryData::download`] or
+ /// [`RegistryData::finish_download`] before calling this method.
fn get_pkg(&mut self, package: PackageId, path: &File) -> CargoResult<Package> {
let path = self
.unpack_package(package, path)
@@ -996,6 +885,11 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
}
+impl RegistryConfig {
+ /// File name of [`RegistryConfig`].
+ const NAME: &str = "config.json";
+}
+
/// Get the maximum upack size that Cargo permits
/// based on a given `size` of your compressed file.
///
@@ -1017,6 +911,9 @@ impl<'cfg> Source for RegistrySource<'cfg> {
fn max_unpack_size(config: &Config, size: u64) -> u64 {
const SIZE_VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE";
const RATIO_VAR: &str = "__CARGO_TEST_MAX_UNPACK_RATIO";
+ const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024; // 512 MiB
+ const MAX_COMPRESSION_RATIO: usize = 20; // 20:1
+
let max_unpack_size = if cfg!(debug_assertions) && config.get_env(SIZE_VAR).is_ok() {
// For integration test only.
config
@@ -1041,30 +938,6 @@ fn max_unpack_size(config: &Config, size: u64) -> u64 {
u64::max(max_unpack_size, size * max_compression_ratio as u64)
}
-fn make_dep_prefix(name: &str) -> String {
- match name.len() {
- 1 => String::from("1"),
- 2 => String::from("2"),
- 3 => format!("3/{}", &name[..1]),
- _ => format!("{}/{}", &name[0..2], &name[2..4]),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::make_dep_prefix;
-
- #[test]
- fn dep_prefix() {
- assert_eq!(make_dep_prefix("a"), "1");
- assert_eq!(make_dep_prefix("ab"), "2");
- assert_eq!(make_dep_prefix("abc"), "3/a");
- assert_eq!(make_dep_prefix("Abc"), "3/A");
- assert_eq!(make_dep_prefix("AbCd"), "Ab/Cd");
- assert_eq!(make_dep_prefix("aBcDe"), "aB/cD");
- }
-}
-
/// Set the current [`umask`] value for the given tarball. No-op on non-Unix
/// platforms.
///
diff --git a/src/tools/cargo/src/cargo/sources/registry/remote.rs b/src/tools/cargo/src/cargo/sources/registry/remote.rs
index f4df4e86b..4223b0303 100644
--- a/src/tools/cargo/src/cargo/sources/registry/remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs
@@ -1,3 +1,5 @@
+//! Access to a Git index based registry. See [`RemoteRegistry`] for details.
+
use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::git::fetch::RemoteKind;
@@ -21,29 +23,73 @@ use std::task::{ready, Poll};
/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
+///
+/// This type is primarily accessed through the [`RegistryData`] trait.
+///
+/// See the [module-level documentation](super) for the index format and layout.
+///
+/// ## History of Git-based index registry
+///
+/// Using Git to host this index used to be quite efficient. The full index can
+/// be stored efficiently locally on disk, and once it is downloaded, all
+/// queries of a registry can happen locally and needn't touch the network.
+/// Git-based index was a reasonable design choice at the time when HTTP/2
+/// was just introduced.
+///
+/// However, the full index keeps growing as crates.io grows. It becomes
+/// relatively big and slows down the first use of Cargo. Git (specifically
+/// libgit2) is not efficient at handling huge amounts of small files either.
+/// On the other hand, newer protocols like HTTP/2 are prevalent and capable to
+/// serve a bunch of tiny files. Today, it is encouraged to use [`HttpRegistry`],
+/// which is the default from 1.70.0. That being said, Cargo will continue
+/// supporting Git-based index for a pretty long while.
+///
+/// [`HttpRegistry`]: super::http_remote::HttpRegistry
pub struct RemoteRegistry<'cfg> {
+ /// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`).
index_path: Filesystem,
- /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
+ /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/cache/$REG-HASH`).
cache_path: Filesystem,
+ /// The unique identifier of this registry source.
source_id: SourceId,
+ /// This reference is stored so that when a registry needs update, it knows
+ /// where to fetch from.
index_git_ref: GitReference,
config: &'cfg Config,
+ /// A Git [tree object] to help this registry find crate metadata from the
+ /// underlying Git repository.
+ ///
+ /// This is stored here to prevent Git from repeatly creating a tree object
+ /// during each call into `load()`.
+ ///
+ /// [tree object]: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects
tree: RefCell<Option<git2::Tree<'static>>>,
+ /// A Git repository that contains the actual index we want.
repo: LazyCell<git2::Repository>,
+ /// The current HEAD commit of the underlying Git repository.
head: Cell<Option<git2::Oid>>,
+ /// This stores sha value of the current HEAD commit for convenience.
current_sha: Cell<Option<InternedString>>,
- needs_update: bool, // Does this registry need to be updated?
+ /// Whether this registry needs to update package informations.
+ ///
+ /// See [`RemoteRegistry::mark_updated`] on how to make sure a registry
+ /// index is updated only once per session.
+ needs_update: bool,
+ /// Disables status messages.
quiet: bool,
}
impl<'cfg> RemoteRegistry<'cfg> {
+ /// Creates a Git-rebased remote registry for `source_id`.
+ ///
+ /// * `name` --- Name of a path segment where `.crate` tarballs and the
+ /// registry index are stored. Expect to be unique.
pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
RemoteRegistry {
index_path: config.registry_index_path().join(name),
cache_path: config.registry_cache_path().join(name),
source_id,
config,
- // TODO: we should probably make this configurable
index_git_ref: GitReference::DefaultBranch,
tree: RefCell::new(None),
repo: LazyCell::new(),
@@ -54,18 +100,12 @@ impl<'cfg> RemoteRegistry<'cfg> {
}
}
+ /// Creates intermediate dirs and initialize the repository.
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);
- // Fast path without a lock
- if let Ok(repo) = git2::Repository::open(&path) {
- trace!("opened a repo without a lock");
- return Ok(repo);
- }
-
- // Ok, now we need to lock and try the whole thing over again.
- trace!("acquiring registry index lock");
match git2::Repository::open(&path) {
Ok(repo) => Ok(repo),
Err(_) => {
@@ -97,6 +137,7 @@ impl<'cfg> RemoteRegistry<'cfg> {
})
}
+ /// Get the object ID of the HEAD commit from the underlying Git repository.
fn head(&self) -> CargoResult<git2::Oid> {
if self.head.get().is_none() {
let repo = self.repo()?;
@@ -106,6 +147,8 @@ impl<'cfg> RemoteRegistry<'cfg> {
Ok(self.head.get().unwrap())
}
+ /// Returns a [`git2::Tree`] object of the current HEAD commit of the
+ /// underlying Git repository.
fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
{
let tree = self.tree.borrow();
@@ -117,6 +160,7 @@ impl<'cfg> RemoteRegistry<'cfg> {
let commit = repo.find_commit(self.head()?)?;
let tree = commit.tree()?;
+ // SAFETY:
// Unfortunately in libgit2 the tree objects look like they've got a
// reference to the repository object which means that a tree cannot
// outlive the repository that it came from. Here we want to cache this
@@ -134,6 +178,9 @@ impl<'cfg> RemoteRegistry<'cfg> {
Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
}
+ /// Gets the current version of the registry index.
+ ///
+ /// It is usually sha of the HEAD commit from the underlying Git repository.
fn current_version(&self) -> Option<InternedString> {
if let Some(sha) = self.current_sha.get() {
return Some(sha);
@@ -143,20 +190,24 @@ impl<'cfg> RemoteRegistry<'cfg> {
Some(sha)
}
+ /// Whether the registry is up-to-date. See [`Self::mark_updated`] for more.
fn is_updated(&self) -> bool {
self.config.updated_sources().contains(&self.source_id)
}
+ /// Marks this registry as up-to-date.
+ ///
+ /// This makes sure the index is only updated once per session since it is
+ /// an expensive operation. This generally only happens when the resolver
+ /// is run multiple times, such as during `cargo publish`.
fn mark_updated(&self) {
self.config.updated_sources().insert(self.source_id);
}
}
-const LAST_UPDATED_FILE: &str = ".last-updated";
-
impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
fn prepare(&self) -> CargoResult<()> {
- self.repo()?; // create intermediate dirs and initialize the repo
+ self.repo()?;
Ok(())
}
@@ -168,13 +219,20 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
self.config.assert_package_cache_locked(path)
}
- // `index_version` Is a string representing the version of the file used to construct the cached copy.
- // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`.
- // This is technically correct but a little too conservative. If a new commit is fetched all cached
- // files need to be regenerated even if a particular file was not changed.
- // However if an old cargo has written such a file we still know how to read it, as long as we check for that hash value.
- //
- // Cargo now uses a hash of the file's contents as provided by git.
+ /// Read the general concept for `load()` on [`RegistryData::load`].
+ ///
+ /// `index_version` is a string representing the version of the file used
+ /// to construct the cached copy.
+ ///
+ /// Older versions of Cargo used the single value of the hash of the HEAD
+ /// commit as a `index_version`. This is technically correct but a little
+ /// too conservative. If a new commit is fetched all cached files need to
+ /// be regenerated even if a particular file was not changed.
+ ///
+ /// However if an old cargo has written such a file we still know how to
+ /// read it, as long as we check for that hash value.
+ ///
+ /// Cargo now uses a hash of the file's contents as provided by git.
fn load(
&mut self,
_root: &Path,
@@ -187,7 +245,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
// Check if the cache is valid.
let git_commit_hash = self.current_version();
if index_version.is_some() && index_version == git_commit_hash.as_deref() {
- // This file was written by an old version of cargo, but it is still up-to-date.
+ // This file was written by an old version of cargo, but it is
+ // still up-to-date.
return Poll::Ready(Ok(LoadResponse::CacheValid));
}
// Note that the index calls this method and the filesystem is locked
@@ -224,8 +283,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
match load_helper(&self, path, index_version) {
Ok(result) => Poll::Ready(Ok(result)),
Err(_) if !self.is_updated() => {
- // If git returns an error and we haven't updated the repo, return
- // pending to allow an update to try again.
+ // If git returns an error and we haven't updated the repo,
+ // return pending to allow an update to try again.
self.needs_update = true;
Poll::Pending
}
@@ -245,7 +304,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
debug!("loading config");
self.prepare()?;
self.config.assert_package_cache_locked(&self.index_path);
- match ready!(self.load(Path::new(""), Path::new("config.json"), None)?) {
+ match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) {
LoadResponse::Data { raw_data, .. } => {
trace!("config loaded");
let mut cfg: RegistryConfig = serde_json::from_slice(&raw_data)?;
@@ -265,9 +324,6 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
self.needs_update = false;
- // Make sure the index is only updated once per session since it is an
- // expensive operation. This generally only happens when the resolver
- // is run multiple times, such as during `cargo publish`.
if self.is_updated() {
return Ok(());
}
@@ -294,7 +350,7 @@ 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(&self.index_path);
if !self.quiet {
self.config
.shell()
@@ -314,15 +370,14 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
)
.with_context(|| format!("failed to fetch `{}`", url))?;
- // Create a dummy file to record the mtime for when we updated the
- // index.
- paths::create(&path.join(LAST_UPDATED_FILE))?;
-
Ok(())
}
+ /// Read the general concept for `invalidate_cache()` on
+ /// [`RegistryData::invalidate_cache`].
+ ///
+ /// To fully invalidate, undo [`RemoteRegistry::mark_updated`]'s work.
fn invalidate_cache(&mut self) {
- // To fully invalidate, undo `mark_updated`s work
self.needs_update = true;
}
@@ -365,9 +420,10 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
}
}
+/// Implemented to just be sure to drop `tree` field before our other fields.
+/// See SAFETY inside [`RemoteRegistry::tree()`] for more.
impl<'cfg> Drop for RemoteRegistry<'cfg> {
fn drop(&mut self) {
- // Just be sure to drop this before our other fields
self.tree.borrow_mut().take();
}
}
diff --git a/src/tools/cargo/src/cargo/util/auth/asymmetric.rs b/src/tools/cargo/src/cargo/util/auth/asymmetric.rs
new file mode 100644
index 000000000..50882a745
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/auth/asymmetric.rs
@@ -0,0 +1,155 @@
+//! Registry asymmetric authentication support. See [RFC 3231] for more.
+//!
+//! [RFC 3231]: https://rust-lang.github.io/rfcs/3231-cargo-asymmetric-tokens.html
+
+use pasetors::keys::AsymmetricPublicKey;
+use pasetors::keys::AsymmetricSecretKey;
+use pasetors::paserk;
+use pasetors::paserk::FormatAsPaserk;
+use pasetors::version3;
+use pasetors::version3::PublicToken;
+use time::format_description::well_known::Rfc3339;
+use time::OffsetDateTime;
+
+use crate::core::SourceId;
+use crate::ops::RegistryCredentialConfig;
+use crate::CargoResult;
+
+use super::Mutation;
+use super::Secret;
+
+/// The main body of an asymmetric token as describe in RFC 3231.
+#[derive(serde::Serialize)]
+struct Message<'a> {
+ iat: &'a str,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ sub: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ mutation: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ name: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ vers: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ cksum: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ challenge: Option<&'a str>,
+ /// This field is not yet used. This field can be set to a value >1 to
+ /// indicate a breaking change in the token format.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ v: Option<u8>,
+}
+
+/// The footer of an asymmetric token as describe in RFC 3231.
+#[derive(serde::Serialize)]
+struct Footer<'a> {
+ url: &'a str,
+ kip: paserk::Id,
+}
+
+/// Checks that a secret key is valid, and returns the associated public key in
+/// Paserk format.
+pub fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> {
+ let secret: Secret<AsymmetricSecretKey<version3::V3>> =
+ secret_key.map(|key| key.try_into()).transpose().ok()?;
+ let public: AsymmetricPublicKey<version3::V3> = secret
+ .as_ref()
+ .map(|key| key.try_into())
+ .transpose()
+ .ok()?
+ .expose();
+ let mut paserk_pub_key = String::new();
+ FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap();
+ Some(paserk_pub_key)
+}
+
+/// Generates a public token from a registry's `credential` configuration for
+/// authenticating to a `source_id`
+///
+/// An optional `mutation` for authenticating a mutation operation aganist the
+/// registry.
+pub fn public_token_from_credential(
+ credential: RegistryCredentialConfig,
+ source_id: &SourceId,
+ mutation: Option<&'_ Mutation<'_>>,
+) -> CargoResult<Secret<String>> {
+ let RegistryCredentialConfig::AsymmetricKey((secret_key, secret_key_subject)) = credential else {
+ anyhow::bail!("credential must be an asymmetric secret key")
+ };
+
+ let secret: Secret<AsymmetricSecretKey<version3::V3>> =
+ secret_key.map(|key| key.as_str().try_into()).transpose()?;
+ let public: AsymmetricPublicKey<version3::V3> = secret
+ .as_ref()
+ .map(|key| key.try_into())
+ .transpose()?
+ .expose();
+ let kip = (&public).try_into()?;
+ let iat = OffsetDateTime::now_utc();
+
+ let message = Message {
+ iat: &iat.format(&Rfc3339)?,
+ sub: secret_key_subject.as_deref(),
+ mutation: mutation.and_then(|m| {
+ Some(match m {
+ Mutation::PrePublish => return None,
+ Mutation::Publish { .. } => "publish",
+ Mutation::Yank { .. } => "yank",
+ Mutation::Unyank { .. } => "unyank",
+ Mutation::Owners { .. } => "owners",
+ })
+ }),
+ name: mutation.and_then(|m| {
+ Some(match m {
+ Mutation::PrePublish => return None,
+ Mutation::Publish { name, .. }
+ | Mutation::Yank { name, .. }
+ | Mutation::Unyank { name, .. }
+ | Mutation::Owners { name, .. } => *name,
+ })
+ }),
+ vers: mutation.and_then(|m| {
+ Some(match m {
+ Mutation::PrePublish | Mutation::Owners { .. } => return None,
+ Mutation::Publish { vers, .. }
+ | Mutation::Yank { vers, .. }
+ | Mutation::Unyank { vers, .. } => *vers,
+ })
+ }),
+ cksum: mutation.and_then(|m| {
+ Some(match m {
+ Mutation::PrePublish
+ | Mutation::Yank { .. }
+ | Mutation::Unyank { .. }
+ | Mutation::Owners { .. } => return None,
+ Mutation::Publish { cksum, .. } => *cksum,
+ })
+ }),
+ challenge: None, // todo: PASETO with challenges
+ v: None,
+ };
+
+ let footer = Footer {
+ url: &source_id.url().to_string(),
+ kip,
+ };
+
+ let secret = secret
+ .map(|secret| {
+ PublicToken::sign(
+ &secret,
+ serde_json::to_string(&message)
+ .expect("cannot serialize")
+ .as_bytes(),
+ Some(
+ serde_json::to_string(&footer)
+ .expect("cannot serialize")
+ .as_bytes(),
+ ),
+ None,
+ )
+ })
+ .transpose()?;
+
+ Ok(secret)
+}
diff --git a/src/tools/cargo/src/cargo/util/auth.rs b/src/tools/cargo/src/cargo/util/auth/mod.rs
index f19acaebe..58309964f 100644
--- a/src/tools/cargo/src/cargo/util/auth.rs
+++ b/src/tools/cargo/src/cargo/util/auth/mod.rs
@@ -1,11 +1,11 @@
//! Registry authentication support.
+mod asymmetric;
+
use crate::util::{config, config::ConfigKey, CanonicalUrl, CargoResult, Config, IntoUrl};
use anyhow::{bail, format_err, Context as _};
use cargo_util::ProcessError;
use core::fmt;
-use pasetors::keys::{AsymmetricPublicKey, AsymmetricSecretKey};
-use pasetors::paserk::FormatAsPaserk;
use serde::Deserialize;
use std::collections::HashMap;
use std::error::Error;
@@ -13,13 +13,13 @@ use std::io::{Read, Write};
use std::ops::Deref;
use std::path::PathBuf;
use std::process::{Command, Stdio};
-use time::format_description::well_known::Rfc3339;
-use time::OffsetDateTime;
use url::Url;
use crate::core::SourceId;
use crate::ops::RegistryCredentialConfig;
+pub use self::asymmetric::paserk_public_from_paserk_secret;
+
use super::config::CredentialCacheValue;
/// A wrapper for values that should not be printed.
@@ -466,82 +466,9 @@ fn auth_token_optional(
run_command(config, &process, sid, Action::Get)?.unwrap();
(independent_of_endpoint, Secret::from(token))
}
- RegistryCredentialConfig::AsymmetricKey((secret_key, secret_key_subject)) => {
- let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> =
- secret_key.map(|key| key.as_str().try_into()).transpose()?;
- let public: AsymmetricPublicKey<pasetors::version3::V3> = secret
- .as_ref()
- .map(|key| key.try_into())
- .transpose()?
- .expose();
- let kip: pasetors::paserk::Id = (&public).try_into()?;
- let iat = OffsetDateTime::now_utc();
-
- let message = Message {
- iat: &iat.format(&Rfc3339)?,
- sub: secret_key_subject.as_deref(),
- mutation: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish => return None,
- Mutation::Publish { .. } => "publish",
- Mutation::Yank { .. } => "yank",
- Mutation::Unyank { .. } => "unyank",
- Mutation::Owners { .. } => "owners",
- })
- }),
- name: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish => return None,
- Mutation::Publish { name, .. }
- | Mutation::Yank { name, .. }
- | Mutation::Unyank { name, .. }
- | Mutation::Owners { name, .. } => *name,
- })
- }),
- vers: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish | Mutation::Owners { .. } => return None,
- Mutation::Publish { vers, .. }
- | Mutation::Yank { vers, .. }
- | Mutation::Unyank { vers, .. } => *vers,
- })
- }),
- cksum: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish
- | Mutation::Yank { .. }
- | Mutation::Unyank { .. }
- | Mutation::Owners { .. } => return None,
- Mutation::Publish { cksum, .. } => *cksum,
- })
- }),
- challenge: None, // todo: PASETO with challenges
- v: None,
- };
- let footer = Footer {
- url: &sid.url().to_string(),
- kip,
- };
-
- (
- false,
- secret
- .map(|secret| {
- pasetors::version3::PublicToken::sign(
- &secret,
- serde_json::to_string(&message)
- .expect("cannot serialize")
- .as_bytes(),
- Some(
- serde_json::to_string(&footer)
- .expect("cannot serialize")
- .as_bytes(),
- ),
- None,
- )
- })
- .transpose()?,
- )
+ cred @ RegistryCredentialConfig::AsymmetricKey(..) => {
+ let token = asymmetric::public_token_from_credential(cred, sid, mutation)?;
+ (false, token)
}
};
@@ -595,33 +522,6 @@ pub enum Mutation<'a> {
},
}
-/// The main body of an asymmetric token as describe in RFC 3231.
-#[derive(serde::Serialize)]
-struct Message<'a> {
- iat: &'a str,
- #[serde(skip_serializing_if = "Option::is_none")]
- sub: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- mutation: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- name: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- vers: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- cksum: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- challenge: Option<&'a str>,
- /// This field is not yet used. This field can be set to a value >1 to indicate a breaking change in the token format.
- #[serde(skip_serializing_if = "Option::is_none")]
- v: Option<u8>,
-}
-/// The footer of an asymmetric token as describe in RFC 3231.
-#[derive(serde::Serialize)]
-struct Footer<'a> {
- url: &'a str,
- kip: pasetors::paserk::Id,
-}
-
enum Action {
Get,
Store(String),
@@ -646,21 +546,6 @@ pub fn login(config: &Config, sid: &SourceId, token: RegistryCredentialConfig) -
Ok(())
}
-/// Checks that a secret key is valid, and returns the associated public key in Paserk format.
-pub(crate) fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> {
- let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> =
- secret_key.map(|key| key.try_into()).transpose().ok()?;
- let public: AsymmetricPublicKey<pasetors::version3::V3> = secret
- .as_ref()
- .map(|key| key.try_into())
- .transpose()
- .ok()?
- .expose();
- let mut paserk_pub_key = String::new();
- FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap();
- Some(paserk_pub_key)
-}
-
/// Removes the token for the given registry.
pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> {
match registry_credential_config(config, sid)? {
diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs
index c18785c66..46ed7dd7c 100644
--- a/src/tools/cargo/src/cargo/util/command_prelude.rs
+++ b/src/tools/cargo/src/cargo/util/command_prelude.rs
@@ -15,6 +15,7 @@ use crate::CargoResult;
use anyhow::bail;
use cargo_util::paths;
use std::ffi::{OsStr, OsString};
+use std::path::Path;
use std::path::PathBuf;
pub use crate::core::compiler::CompileMode;
@@ -23,6 +24,8 @@ pub use clap::{value_parser, Arg, ArgAction, ArgMatches};
pub use clap::Command;
+use super::config::JobsConfig;
+
pub trait CommandExt: Sized {
fn _arg(self, arg: Arg) -> Self;
@@ -66,7 +69,7 @@ pub trait CommandExt: Sized {
fn arg_jobs(self) -> Self {
self._arg(
- opt("jobs", "Number of parallel jobs, defaults to # of CPUs")
+ opt("jobs", "Number of parallel jobs, defaults to # of CPUs.")
.short('j')
.value_name("N")
.allow_hyphen_values(true),
@@ -337,22 +340,7 @@ pub trait ArgMatchesExt {
}
fn root_manifest(&self, config: &Config) -> CargoResult<PathBuf> {
- if let Some(path) = self.value_of_path("manifest-path", config) {
- // In general, we try to avoid normalizing paths in Cargo,
- // but in this particular case we need it to fix #3586.
- let path = paths::normalize_path(&path);
- if !path.ends_with("Cargo.toml") {
- anyhow::bail!("the manifest-path must be a path to a Cargo.toml file")
- }
- if !path.exists() {
- anyhow::bail!(
- "manifest path `{}` does not exist",
- self._value_of("manifest-path").unwrap()
- )
- }
- return Ok(path);
- }
- find_root_manifest_for_wd(config.cwd())
+ root_manifest(self._value_of("manifest-path").map(Path::new), config)
}
fn workspace<'a>(&self, config: &'a Config) -> CargoResult<Workspace<'a>> {
@@ -364,8 +352,16 @@ pub trait ArgMatchesExt {
Ok(ws)
}
- fn jobs(&self) -> CargoResult<Option<i32>> {
- self.value_of_i32("jobs")
+ fn jobs(&self) -> CargoResult<Option<JobsConfig>> {
+ let arg = match self._value_of("jobs") {
+ None => None,
+ Some(arg) => match arg.parse::<i32>() {
+ Ok(j) => Some(JobsConfig::Integer(j)),
+ Err(_) => Some(JobsConfig::String(arg.to_string())),
+ },
+ };
+
+ Ok(arg)
}
fn verbose(&self) -> u32 {
@@ -782,6 +778,33 @@ pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> {
args._values_of_os(name)
}
+pub fn root_manifest(manifest_path: Option<&Path>, config: &Config) -> CargoResult<PathBuf> {
+ if let Some(manifest_path) = manifest_path {
+ let path = config.cwd().join(manifest_path);
+ // In general, we try to avoid normalizing paths in Cargo,
+ // but in this particular case we need it to fix #3586.
+ let path = paths::normalize_path(&path);
+ if !path.ends_with("Cargo.toml") && !crate::util::toml::is_embedded(&path) {
+ anyhow::bail!("the manifest-path must be a path to a Cargo.toml file")
+ }
+ if !path.exists() {
+ anyhow::bail!("manifest path `{}` does not exist", manifest_path.display())
+ }
+ if path.is_dir() {
+ anyhow::bail!(
+ "manifest path `{}` is a directory but expected a file",
+ manifest_path.display()
+ )
+ }
+ if crate::util::toml::is_embedded(&path) && !config.cli_unstable().script {
+ anyhow::bail!("embedded manifest `{}` requires `-Zscript`", path.display())
+ }
+ Ok(path)
+ } else {
+ find_root_manifest_for_wd(config.cwd())
+ }
+}
+
#[track_caller]
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
match r {
diff --git a/src/tools/cargo/src/cargo/util/config/mod.rs b/src/tools/cargo/src/cargo/util/config/mod.rs
index 076b78299..4e6bca302 100644
--- a/src/tools/cargo/src/cargo/util/config/mod.rs
+++ b/src/tools/cargo/src/cargo/util/config/mod.rs
@@ -69,9 +69,11 @@ use self::ConfigValue as CV;
use crate::core::compiler::rustdoc::RustdocExternMap;
use crate::core::shell::Verbosity;
use crate::core::{features, CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig};
-use crate::ops::{self, RegistryCredentialConfig};
+use crate::ops::RegistryCredentialConfig;
use crate::util::auth::Secret;
use crate::util::errors::CargoResult;
+use crate::util::network::http::configure_http_handle;
+use crate::util::network::http::http_handle;
use crate::util::CanonicalUrl;
use crate::util::{internal, toml as cargo_toml};
use crate::util::{try_canonicalize, validate_package_name};
@@ -363,7 +365,7 @@ impl Config {
self.registry_base_path().join("index")
}
- /// Gets the Cargo registry cache directory (`<cargo_home>/registry/path`).
+ /// Gets the Cargo registry cache directory (`<cargo_home>/registry/cache`).
pub fn registry_cache_path(&self) -> Filesystem {
self.registry_base_path().join("cache")
}
@@ -1273,6 +1275,16 @@ impl Config {
return Ok(Vec::new());
}
};
+
+ for (path, abs_path, def) in &includes {
+ if abs_path.extension() != Some(OsStr::new("toml")) {
+ bail!(
+ "expected a config include path ending with `.toml`, \
+ but found `{path}` from `{def}`",
+ )
+ }
+ }
+
Ok(includes)
}
@@ -1706,11 +1718,11 @@ impl Config {
pub fn http(&self) -> CargoResult<&RefCell<Easy>> {
let http = self
.easy
- .try_borrow_with(|| ops::http_handle(self).map(RefCell::new))?;
+ .try_borrow_with(|| http_handle(self).map(RefCell::new))?;
{
let mut http = http.borrow_mut();
http.reset();
- let timeout = ops::configure_http_handle(self, &mut http)?;
+ let timeout = configure_http_handle(self, &mut http)?;
timeout.configure(&mut http)?;
}
Ok(http)
@@ -2453,6 +2465,25 @@ pub struct CargoSshConfig {
pub known_hosts: Option<Vec<Value<String>>>,
}
+/// Configuration for `jobs` in `build` section. There are two
+/// ways to configure: An integer or a simple string expression.
+///
+/// ```toml
+/// [build]
+/// jobs = 1
+/// ```
+///
+/// ```toml
+/// [build]
+/// jobs = "default" # Currently only support "default".
+/// ```
+#[derive(Debug, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum JobsConfig {
+ Integer(i32),
+ String(String),
+}
+
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoBuildConfig {
@@ -2462,7 +2493,7 @@ pub struct CargoBuildConfig {
pub target_dir: Option<ConfigRelativePath>,
pub incremental: Option<bool>,
pub target: Option<BuildTargetConfig>,
- pub jobs: Option<i32>,
+ pub jobs: Option<JobsConfig>,
pub rustflags: Option<StringList>,
pub rustdocflags: Option<StringList>,
pub rustc_wrapper: Option<ConfigRelativePath>,
diff --git a/src/tools/cargo/src/cargo/util/config/target.rs b/src/tools/cargo/src/cargo/util/config/target.rs
index a7f2f3ef2..cdafe73dd 100644
--- a/src/tools/cargo/src/cargo/util/config/target.rs
+++ b/src/tools/cargo/src/cargo/util/config/target.rs
@@ -1,5 +1,5 @@
use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV};
-use crate::core::compiler::{BuildOutput, LinkType};
+use crate::core::compiler::{BuildOutput, LinkArgTarget};
use crate::util::CargoResult;
use serde::Deserialize;
use std::collections::{BTreeMap, HashMap};
@@ -178,27 +178,27 @@ fn parse_links_overrides(
.extend(list.iter().map(|v| PathBuf::from(&v.0)));
}
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
- let args = extra_link_args(LinkType::Cdylib, key, value)?;
+ let args = extra_link_args(LinkArgTarget::Cdylib, key, value)?;
output.linker_args.extend(args);
}
"rustc-link-arg-bins" => {
- let args = extra_link_args(LinkType::Bin, key, value)?;
+ let args = extra_link_args(LinkArgTarget::Bin, key, value)?;
output.linker_args.extend(args);
}
"rustc-link-arg" => {
- let args = extra_link_args(LinkType::All, key, value)?;
+ let args = extra_link_args(LinkArgTarget::All, key, value)?;
output.linker_args.extend(args);
}
"rustc-link-arg-tests" => {
- let args = extra_link_args(LinkType::Test, key, value)?;
+ let args = extra_link_args(LinkArgTarget::Test, key, value)?;
output.linker_args.extend(args);
}
"rustc-link-arg-benches" => {
- let args = extra_link_args(LinkType::Bench, key, value)?;
+ let args = extra_link_args(LinkArgTarget::Bench, key, value)?;
output.linker_args.extend(args);
}
"rustc-link-arg-examples" => {
- let args = extra_link_args(LinkType::Example, key, value)?;
+ let args = extra_link_args(LinkArgTarget::Example, key, value)?;
output.linker_args.extend(args);
}
"rustc-cfg" => {
@@ -237,10 +237,10 @@ fn parse_links_overrides(
}
fn extra_link_args<'a>(
- link_type: LinkType,
+ link_type: LinkArgTarget,
key: &str,
value: &'a CV,
-) -> CargoResult<impl Iterator<Item = (LinkType, String)> + 'a> {
+) -> CargoResult<impl Iterator<Item = (LinkArgTarget, String)> + 'a> {
let args = value.list(key)?;
Ok(args.iter().map(move |v| (link_type.clone(), v.0.clone())))
}
diff --git a/src/tools/cargo/src/cargo/util/interning.rs b/src/tools/cargo/src/cargo/util/interning.rs
index bbec12942..584fdf623 100644
--- a/src/tools/cargo/src/cargo/util/interning.rs
+++ b/src/tools/cargo/src/cargo/util/interning.rs
@@ -10,14 +10,13 @@ use std::path::Path;
use std::ptr;
use std::str;
use std::sync::Mutex;
+use std::sync::OnceLock;
fn leak(s: String) -> &'static str {
Box::leak(s.into_boxed_str())
}
-lazy_static::lazy_static! {
- static ref STRING_CACHE: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new());
-}
+static STRING_CACHE: OnceLock<Mutex<HashSet<&'static str>>> = OnceLock::new();
#[derive(Clone, Copy)]
pub struct InternedString {
@@ -64,7 +63,10 @@ impl Eq for InternedString {}
impl InternedString {
pub fn new(str: &str) -> InternedString {
- let mut cache = STRING_CACHE.lock().unwrap();
+ let mut cache = STRING_CACHE
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
let s = cache.get(str).cloned().unwrap_or_else(|| {
let s = leak(str.to_string());
cache.insert(s);
diff --git a/src/tools/cargo/src/cargo/util/network/http.rs b/src/tools/cargo/src/cargo/util/network/http.rs
new file mode 100644
index 000000000..f077ce2b6
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/network/http.rs
@@ -0,0 +1,216 @@
+//! Configures libcurl's http handles.
+
+use std::str;
+use std::time::Duration;
+
+use anyhow::bail;
+use curl::easy::Easy;
+use curl::easy::InfoType;
+use curl::easy::SslOpt;
+use curl::easy::SslVersion;
+use log::log;
+use log::Level;
+
+use crate::util::config::SslVersionConfig;
+use crate::util::config::SslVersionConfigRange;
+use crate::version;
+use crate::CargoResult;
+use crate::Config;
+
+/// Creates a new HTTP handle with appropriate global configuration for cargo.
+pub fn http_handle(config: &Config) -> CargoResult<Easy> {
+ let (mut handle, timeout) = http_handle_and_timeout(config)?;
+ timeout.configure(&mut handle)?;
+ Ok(handle)
+}
+
+pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeout)> {
+ if config.frozen() {
+ bail!(
+ "attempting to make an HTTP request, but --frozen was \
+ specified"
+ )
+ }
+ if config.offline() {
+ bail!(
+ "attempting to make an HTTP request, but --offline was \
+ specified"
+ )
+ }
+
+ // The timeout option for libcurl by default times out the entire transfer,
+ // but we probably don't want this. Instead we only set timeouts for the
+ // connect phase as well as a "low speed" timeout so if we don't receive
+ // many bytes in a large-ish period of time then we time out.
+ let mut handle = Easy::new();
+ let timeout = configure_http_handle(config, &mut handle)?;
+ Ok((handle, timeout))
+}
+
+// Only use a custom transport if any HTTP options are specified,
+// such as proxies or custom certificate authorities.
+//
+// The custom transport, however, is not as well battle-tested.
+pub fn needs_custom_http_transport(config: &Config) -> CargoResult<bool> {
+ Ok(
+ super::proxy::http_proxy_exists(config.http_config()?, config)
+ || *config.http_config()? != Default::default()
+ || config.get_env_os("HTTP_TIMEOUT").is_some(),
+ )
+}
+
+/// Configure a libcurl http handle with the defaults options for Cargo
+pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<HttpTimeout> {
+ let http = config.http_config()?;
+ if let Some(proxy) = super::proxy::http_proxy(http) {
+ handle.proxy(&proxy)?;
+ }
+ if let Some(cainfo) = &http.cainfo {
+ let cainfo = cainfo.resolve_path(config);
+ handle.cainfo(&cainfo)?;
+ }
+ if let Some(check) = http.check_revoke {
+ handle.ssl_options(SslOpt::new().no_revoke(!check))?;
+ }
+
+ if let Some(user_agent) = &http.user_agent {
+ handle.useragent(user_agent)?;
+ } else {
+ handle.useragent(&format!("cargo {}", version()))?;
+ }
+
+ fn to_ssl_version(s: &str) -> CargoResult<SslVersion> {
+ let version = match s {
+ "default" => SslVersion::Default,
+ "tlsv1" => SslVersion::Tlsv1,
+ "tlsv1.0" => SslVersion::Tlsv10,
+ "tlsv1.1" => SslVersion::Tlsv11,
+ "tlsv1.2" => SslVersion::Tlsv12,
+ "tlsv1.3" => SslVersion::Tlsv13,
+ _ => bail!(
+ "Invalid ssl version `{s}`,\
+ choose from 'default', 'tlsv1', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'."
+ ),
+ };
+ Ok(version)
+ }
+
+ // Empty string accept encoding expands to the encodings supported by the current libcurl.
+ handle.accept_encoding("")?;
+ if let Some(ssl_version) = &http.ssl_version {
+ match ssl_version {
+ SslVersionConfig::Single(s) => {
+ let version = to_ssl_version(s.as_str())?;
+ handle.ssl_version(version)?;
+ }
+ SslVersionConfig::Range(SslVersionConfigRange { min, max }) => {
+ let min_version = min
+ .as_ref()
+ .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?;
+ let max_version = max
+ .as_ref()
+ .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?;
+ handle.ssl_min_max_version(min_version, max_version)?;
+ }
+ }
+ } else if cfg!(windows) {
+ // This is a temporary workaround for some bugs with libcurl and
+ // schannel and TLS 1.3.
+ //
+ // Our libcurl on Windows is usually built with schannel.
+ // On Windows 11 (or Windows Server 2022), libcurl recently (late
+ // 2022) gained support for TLS 1.3 with schannel, and it now defaults
+ // to 1.3. Unfortunately there have been some bugs with this.
+ // https://github.com/curl/curl/issues/9431 is the most recent. Once
+ // that has been fixed, and some time has passed where we can be more
+ // confident that the 1.3 support won't cause issues, this can be
+ // removed.
+ //
+ // Windows 10 is unaffected. libcurl does not support TLS 1.3 on
+ // Windows 10. (Windows 10 sorta had support, but it required enabling
+ // an advanced option in the registry which was buggy, and libcurl
+ // does runtime checks to prevent it.)
+ handle.ssl_min_max_version(SslVersion::Default, SslVersion::Tlsv12)?;
+ }
+
+ if let Some(true) = http.debug {
+ handle.verbose(true)?;
+ log::debug!("{:#?}", curl::Version::get());
+ handle.debug_function(|kind, data| {
+ let (prefix, level) = match kind {
+ InfoType::Text => ("*", Level::Debug),
+ InfoType::HeaderIn => ("<", Level::Debug),
+ InfoType::HeaderOut => (">", Level::Debug),
+ InfoType::DataIn => ("{", Level::Trace),
+ InfoType::DataOut => ("}", Level::Trace),
+ InfoType::SslDataIn | InfoType::SslDataOut => return,
+ _ => return,
+ };
+ let starts_with_ignore_case = |line: &str, text: &str| -> bool {
+ line[..line.len().min(text.len())].eq_ignore_ascii_case(text)
+ };
+ match str::from_utf8(data) {
+ Ok(s) => {
+ for mut line in s.lines() {
+ if starts_with_ignore_case(line, "authorization:") {
+ line = "Authorization: [REDACTED]";
+ } else if starts_with_ignore_case(line, "h2h3 [authorization:") {
+ line = "h2h3 [Authorization: [REDACTED]]";
+ } else if starts_with_ignore_case(line, "set-cookie") {
+ line = "set-cookie: [REDACTED]";
+ }
+ log!(level, "http-debug: {} {}", prefix, line);
+ }
+ }
+ Err(_) => {
+ log!(
+ level,
+ "http-debug: {} ({} bytes of data)",
+ prefix,
+ data.len()
+ );
+ }
+ }
+ })?;
+ }
+
+ HttpTimeout::new(config)
+}
+
+#[must_use]
+pub struct HttpTimeout {
+ pub dur: Duration,
+ pub low_speed_limit: u32,
+}
+
+impl HttpTimeout {
+ pub fn new(config: &Config) -> CargoResult<HttpTimeout> {
+ let http_config = config.http_config()?;
+ let low_speed_limit = http_config.low_speed_limit.unwrap_or(10);
+ let seconds = http_config
+ .timeout
+ .or_else(|| {
+ config
+ .get_env("HTTP_TIMEOUT")
+ .ok()
+ .and_then(|s| s.parse().ok())
+ })
+ .unwrap_or(30);
+ Ok(HttpTimeout {
+ dur: Duration::new(seconds, 0),
+ low_speed_limit,
+ })
+ }
+
+ pub fn configure(&self, handle: &mut Easy) -> CargoResult<()> {
+ // The timeout option for libcurl by default times out the entire
+ // transfer, but we probably don't want this. Instead we only set
+ // timeouts for the connect phase as well as a "low speed" timeout so
+ // if we don't receive many bytes in a large-ish period of time then we
+ // time out.
+ handle.connect_timeout(self.dur)?;
+ handle.low_speed_time(self.dur)?;
+ handle.low_speed_limit(self.low_speed_limit)?;
+ Ok(())
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/network/mod.rs b/src/tools/cargo/src/cargo/util/network/mod.rs
index 2006bb65f..b078fa352 100644
--- a/src/tools/cargo/src/cargo/util/network/mod.rs
+++ b/src/tools/cargo/src/cargo/util/network/mod.rs
@@ -2,6 +2,7 @@
use std::task::Poll;
+pub mod http;
pub mod proxy;
pub mod retry;
pub mod sleep;
@@ -20,20 +21,51 @@ impl<T> PollExt<T> for Poll<T> {
}
}
-// When dynamically linked against libcurl, we want to ignore some failures
-// when using old versions that don't support certain features.
+/// When dynamically linked against libcurl, we want to ignore some failures
+/// when using old versions that don't support certain features.
#[macro_export]
macro_rules! try_old_curl {
($e:expr, $msg:expr) => {
let result = $e;
if cfg!(target_os = "macos") {
if let Err(e) = result {
- warn!("ignoring libcurl {} error: {}", $msg, e);
+ ::log::warn!("ignoring libcurl {} error: {}", $msg, e);
}
} else {
+ use ::anyhow::Context;
result.with_context(|| {
- anyhow::format_err!("failed to enable {}, is curl not built right?", $msg)
+ ::anyhow::format_err!("failed to enable {}, is curl not built right?", $msg)
})?;
}
};
}
+
+/// Enable HTTP/2 and pipewait to be used as it'll allow true multiplexing
+/// which makes downloads much faster.
+///
+/// Currently Cargo requests the `http2` feature of the `curl` crate which
+/// means it should always be built in. On OSX, however, we ship cargo still
+/// linked against the system libcurl. Building curl with ALPN support for
+/// HTTP/2 requires newer versions of OSX (the SecureTransport API) than we
+/// want to ship Cargo for. By linking Cargo against the system libcurl then
+/// older curl installations won't use HTTP/2 but newer ones will. All that to
+/// basically say we ignore errors here on OSX, but consider this a fatal error
+/// to not activate HTTP/2 on all other platforms.
+///
+/// `pipewait` is an option which indicates that if there's a bunch of parallel
+/// requests to the same host they all wait until the pipelining status of the
+/// host is known. This means that we won't initiate dozens of connections but
+/// rather only one. Once the main one is opened we realized that pipelining is
+/// possible and multiplexing is possible. All in all this reduces the number
+/// of connections down to a more manageable state.
+#[macro_export]
+macro_rules! try_old_curl_http2_pipewait {
+ ($multiplexing:expr, $handle:expr) => {
+ if $multiplexing {
+ $crate::try_old_curl!($handle.http_version(curl::easy::HttpVersion::V2), "HTTP/2");
+ } else {
+ $handle.http_version(curl::easy::HttpVersion::V11)?;
+ }
+ $crate::try_old_curl!($handle.pipewait(true), "pipewait");
+ };
+}
diff --git a/src/tools/cargo/src/cargo/util/network/retry.rs b/src/tools/cargo/src/cargo/util/network/retry.rs
index 42c38ab9f..5cb7d1e4f 100644
--- a/src/tools/cargo/src/cargo/util/network/retry.rs
+++ b/src/tools/cargo/src/cargo/util/network/retry.rs
@@ -56,21 +56,29 @@ impl<'a> Retry<'a> {
return RetryResult::Err(e);
}
self.retries += 1;
- let sleep = if self.retries == 1 {
- let mut rng = rand::thread_rng();
- INITIAL_RETRY_SLEEP_BASE_MS + rng.gen_range(0..INITIAL_RETRY_JITTER_MS)
- } else {
- min(
- ((self.retries - 1) * 3) * 1000 + INITIAL_RETRY_SLEEP_BASE_MS,
- MAX_RETRY_SLEEP_MS,
- )
- };
- RetryResult::Retry(sleep)
+ RetryResult::Retry(self.next_sleep_ms())
}
Err(e) => RetryResult::Err(e),
Ok(r) => RetryResult::Success(r),
}
}
+
+ /// Gets the next sleep duration in milliseconds.
+ fn next_sleep_ms(&self) -> u64 {
+ if let Ok(sleep) = self.config.get_env("__CARGO_TEST_FIXED_RETRY_SLEEP_MS") {
+ return sleep.parse().expect("a u64");
+ }
+
+ if self.retries == 1 {
+ let mut rng = rand::thread_rng();
+ INITIAL_RETRY_SLEEP_BASE_MS + rng.gen_range(0..INITIAL_RETRY_JITTER_MS)
+ } else {
+ min(
+ ((self.retries - 1) * 3) * 1000 + INITIAL_RETRY_SLEEP_BASE_MS,
+ MAX_RETRY_SLEEP_MS,
+ )
+ }
+ }
}
fn maybe_spurious(err: &Error) -> bool {
diff --git a/src/tools/cargo/src/cargo/util/profile.rs b/src/tools/cargo/src/cargo/util/profile.rs
index 79b544d98..29b110492 100644
--- a/src/tools/cargo/src/cargo/util/profile.rs
+++ b/src/tools/cargo/src/cargo/util/profile.rs
@@ -1,4 +1,4 @@
-//! # An internal profiler for Cargo itself
+//! An internal performance profiler for Cargo itself.
//!
//! > **Note**: This might not be the module you are looking for.
//! > For information about how Cargo handles compiler flags with profiles,
diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs
index 650ae2330..be1811a88 100644
--- a/src/tools/cargo/src/cargo/util/restricted_names.rs
+++ b/src/tools/cargo/src/cargo/util/restricted_names.rs
@@ -83,6 +83,30 @@ pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult<
Ok(())
}
+/// Ensure a package name is [valid][validate_package_name]
+pub fn sanitize_package_name(name: &str, placeholder: char) -> String {
+ let mut slug = String::new();
+ let mut chars = name.chars();
+ if let Some(ch) = chars.next() {
+ if ch.is_digit(10) {
+ slug.push(placeholder);
+ slug.push(ch);
+ } else if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' {
+ slug.push(ch);
+ } else {
+ slug.push(placeholder);
+ }
+ }
+ for ch in chars {
+ if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' {
+ slug.push(ch);
+ } else {
+ slug.push(placeholder);
+ }
+ }
+ slug
+}
+
/// Check the entire path for names reserved in Windows.
pub fn is_windows_reserved_path(path: &Path) -> bool {
path.iter()
diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs
new file mode 100644
index 000000000..8e41010b4
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs
@@ -0,0 +1,895 @@
+use anyhow::Context as _;
+
+use crate::util::restricted_names;
+use crate::CargoResult;
+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(
+ content: &str,
+ path: &std::path::Path,
+ config: &Config,
+) -> CargoResult<String> {
+ let comment = match extract_comment(content) {
+ Ok(comment) => Some(comment),
+ Err(err) => {
+ log::trace!("failed to extract doc comment: {err}");
+ None
+ }
+ }
+ .unwrap_or_default();
+ let manifest = match extract_manifest(&comment)? {
+ Some(manifest) => Some(manifest),
+ None => {
+ log::trace!("failed to extract manifest");
+ None
+ }
+ }
+ .unwrap_or_default();
+ let manifest = expand_manifest_(&manifest, path, config)
+ .with_context(|| format!("failed to parse manifest at {}", path.display()))?;
+ let manifest = toml::to_string_pretty(&manifest)?;
+ Ok(manifest)
+}
+
+fn expand_manifest_(
+ manifest: &str,
+ path: &std::path::Path,
+ config: &Config,
+) -> CargoResult<toml::Table> {
+ let mut manifest: toml::Table = toml::from_str(&manifest)?;
+
+ for key in ["workspace", "lib", "bin", "example", "test", "bench"] {
+ if manifest.contains_key(key) {
+ anyhow::bail!("`{key}` is not allowed in embedded manifests")
+ }
+ }
+
+ // Prevent looking for a workspace by `read_manifest_from_str`
+ manifest.insert("workspace".to_owned(), toml::Table::new().into());
+
+ let package = manifest
+ .entry("package".to_owned())
+ .or_insert_with(|| toml::Table::new().into())
+ .as_table_mut()
+ .ok_or_else(|| anyhow::format_err!("`package` must be a table"))?;
+ for key in ["workspace", "build", "links"]
+ .iter()
+ .chain(AUTO_FIELDS.iter())
+ {
+ if package.contains_key(*key) {
+ anyhow::bail!("`package.{key}` is not allowed in embedded manifests")
+ }
+ }
+ let file_name = path
+ .file_name()
+ .ok_or_else(|| anyhow::format_err!("no file name"))?
+ .to_string_lossy();
+ let file_stem = path
+ .file_stem()
+ .ok_or_else(|| anyhow::format_err!("no file name"))?
+ .to_string_lossy();
+ let name = sanitize_name(file_stem.as_ref());
+ let bin_name = name.clone();
+ 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 unspecifiead, defaulting to `{}`",
+ DEFAULT_EDITION
+ ));
+ toml::Value::String(DEFAULT_EDITION.to_string())
+ });
+ 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())
+ .or_insert_with(|| toml::Value::Boolean(false));
+ }
+
+ let mut bin = toml::Table::new();
+ bin.insert("name".to_owned(), toml::Value::String(bin_name));
+ bin.insert(
+ "path".to_owned(),
+ toml::Value::String(file_name.into_owned()),
+ );
+ manifest.insert(
+ "bin".to_owned(),
+ toml::Value::Array(vec![toml::Value::Table(bin)]),
+ );
+
+ let release = manifest
+ .entry("profile".to_owned())
+ .or_insert_with(|| toml::Value::Table(Default::default()))
+ .as_table_mut()
+ .ok_or_else(|| anyhow::format_err!("`profile` must be a table"))?
+ .entry("release".to_owned())
+ .or_insert_with(|| toml::Value::Table(Default::default()))
+ .as_table_mut()
+ .ok_or_else(|| anyhow::format_err!("`profile.release` must be a table"))?;
+ release
+ .entry("strip".to_owned())
+ .or_insert_with(|| toml::Value::Boolean(true));
+
+ Ok(manifest)
+}
+
+/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
+fn sanitize_name(name: &str) -> String {
+ let placeholder = if name.contains('_') {
+ '_'
+ } else {
+ // Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the
+ // more common convention for CLIs
+ '-'
+ };
+
+ let mut name = restricted_names::sanitize_package_name(name, placeholder);
+
+ loop {
+ if restricted_names::is_keyword(&name) {
+ name.push(placeholder);
+ } else if restricted_names::is_conflicting_artifact_name(&name) {
+ // Being an embedded manifest, we always assume it is a `[[bin]]`
+ name.push(placeholder);
+ } else if name == "test" {
+ name.push(placeholder);
+ } else if restricted_names::is_windows_reserved(&name) {
+ // Go ahead and be consistent across platforms
+ name.push(placeholder);
+ } else {
+ break;
+ }
+ }
+
+ name
+}
+
+/// Locates a "code block manifest" in Rust source.
+fn extract_comment(input: &str) -> CargoResult<String> {
+ let mut doc_fragments = Vec::new();
+ let file = syn::parse_file(input)?;
+ // HACK: `syn` doesn't tell us what kind of comment was used, so infer it from how many
+ // attributes were used
+ let kind = if 1 < file
+ .attrs
+ .iter()
+ .filter(|attr| attr.meta.path().is_ident("doc"))
+ .count()
+ {
+ CommentKind::Line
+ } else {
+ CommentKind::Block
+ };
+ for attr in &file.attrs {
+ if attr.meta.path().is_ident("doc") {
+ doc_fragments.push(DocFragment::new(attr, kind)?);
+ }
+ }
+ if doc_fragments.is_empty() {
+ anyhow::bail!("no doc-comment found");
+ }
+ unindent_doc_fragments(&mut doc_fragments);
+
+ let mut doc_comment = String::new();
+ for frag in &doc_fragments {
+ add_doc_fragment(&mut doc_comment, frag);
+ }
+
+ Ok(doc_comment)
+}
+
+/// A `#[doc]`
+#[derive(Clone, Debug)]
+struct DocFragment {
+ /// The attribute value
+ doc: String,
+ /// Indentation used within `doc
+ indent: usize,
+}
+
+impl DocFragment {
+ fn new(attr: &syn::Attribute, kind: CommentKind) -> CargoResult<Self> {
+ let syn::Meta::NameValue(nv) = &attr.meta else {
+ anyhow::bail!("unsupported attr meta for {:?}", attr.meta.path())
+ };
+ let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = &nv.value else {
+ anyhow::bail!("only string literals are supported")
+ };
+ Ok(Self {
+ doc: beautify_doc_string(lit.value(), kind),
+ indent: 0,
+ })
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum CommentKind {
+ Line,
+ Block,
+}
+
+/// Makes a doc string more presentable to users.
+/// Used by rustdoc and perhaps other tools, but not by rustc.
+///
+/// See `rustc_ast/util/comments.rs`
+fn beautify_doc_string(data: String, kind: CommentKind) -> String {
+ fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> {
+ let mut i = 0;
+ let mut j = lines.len();
+ // first line of all-stars should be omitted
+ if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
+ i += 1;
+ }
+
+ // like the first, a last line of all stars should be omitted
+ if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') {
+ j -= 1;
+ }
+
+ if i != 0 || j != lines.len() {
+ Some((i, j))
+ } else {
+ None
+ }
+ }
+
+ fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<String> {
+ let mut i = usize::MAX;
+ let mut first = true;
+
+ // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are
+ // present. However, we first need to strip the empty lines so they don't get in the middle
+ // when we try to compute the "horizontal trim".
+ let lines = match kind {
+ CommentKind::Block => {
+ // Whatever happens, we skip the first line.
+ let mut i = lines
+ .get(0)
+ .map(|l| {
+ if l.trim_start().starts_with('*') {
+ 0
+ } else {
+ 1
+ }
+ })
+ .unwrap_or(0);
+ let mut j = lines.len();
+
+ while i < j && lines[i].trim().is_empty() {
+ i += 1;
+ }
+ while j > i && lines[j - 1].trim().is_empty() {
+ j -= 1;
+ }
+ &lines[i..j]
+ }
+ CommentKind::Line => lines,
+ };
+
+ for line in lines {
+ for (j, c) in line.chars().enumerate() {
+ if j > i || !"* \t".contains(c) {
+ return None;
+ }
+ if c == '*' {
+ if first {
+ i = j;
+ first = false;
+ } else if i != j {
+ return None;
+ }
+ break;
+ }
+ }
+ if i >= line.len() {
+ return None;
+ }
+ }
+ if lines.is_empty() {
+ None
+ } else {
+ Some(lines[0][..i].into())
+ }
+ }
+
+ let data_s = data.as_str();
+ if data_s.contains('\n') {
+ let mut lines = data_s.lines().collect::<Vec<&str>>();
+ let mut changes = false;
+ let lines = if let Some((i, j)) = get_vertical_trim(&lines) {
+ changes = true;
+ // remove whitespace-only lines from the start/end of lines
+ &mut lines[i..j]
+ } else {
+ &mut lines
+ };
+ if let Some(horizontal) = get_horizontal_trim(lines, kind) {
+ changes = true;
+ // remove a "[ \t]*\*" block from each line, if possible
+ for line in lines.iter_mut() {
+ if let Some(tmp) = line.strip_prefix(&horizontal) {
+ *line = tmp;
+ if kind == CommentKind::Block
+ && (*line == "*" || line.starts_with("* ") || line.starts_with("**"))
+ {
+ *line = &line[1..];
+ }
+ }
+ }
+ }
+ if changes {
+ return lines.join("\n");
+ }
+ }
+ data
+}
+
+/// Removes excess indentation on comments in order for the Markdown
+/// to be parsed correctly. This is necessary because the convention for
+/// writing documentation is to provide a space between the /// or //! marker
+/// and the doc text, but Markdown is whitespace-sensitive. For example,
+/// a block of text with four-space indentation is parsed as a code block,
+/// so if we didn't unindent comments, these list items
+///
+/// /// A list:
+/// ///
+/// /// - Foo
+/// /// - Bar
+///
+/// would be parsed as if they were in a code block, which is likely not what the user intended.
+///
+/// See also `rustc_resolve/rustdoc.rs`
+fn unindent_doc_fragments(docs: &mut [DocFragment]) {
+ // HACK: We can't tell the difference between `#[doc]` and doc-comments, so we can't specialize
+ // the indentation like rustodc does
+ let add = 0;
+
+ // `min_indent` is used to know how much whitespaces from the start of each lines must be
+ // removed. Example:
+ //
+ // ```
+ // /// hello!
+ // #[doc = "another"]
+ // ```
+ //
+ // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
+ // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
+ // (5 - 1) whitespaces.
+ let Some(min_indent) = docs
+ .iter()
+ .map(|fragment| {
+ fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
+ if line.chars().all(|c| c.is_whitespace()) {
+ min_indent
+ } else {
+ // Compare against either space or tab, ignoring whether they are
+ // mixed or not.
+ let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
+ min_indent.min(whitespace)
+ }
+ })
+ })
+ .min()
+ else {
+ return;
+ };
+
+ for fragment in docs {
+ if fragment.doc.is_empty() {
+ continue;
+ }
+
+ let min_indent = if min_indent > 0 {
+ min_indent - add
+ } else {
+ min_indent
+ };
+
+ fragment.indent = min_indent;
+ }
+}
+
+/// The goal of this function is to apply the `DocFragment` transformation that is required when
+/// transforming into the final Markdown, which is applying the computed indent to each line in
+/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`).
+///
+/// Note: remove the trailing newline where appropriate
+///
+/// See also `rustc_resolve/rustdoc.rs`
+fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
+ let s = frag.doc.as_str();
+ let mut iter = s.lines();
+ if s.is_empty() {
+ out.push('\n');
+ return;
+ }
+ while let Some(line) = iter.next() {
+ if line.chars().any(|c| !c.is_whitespace()) {
+ assert!(line.len() >= frag.indent);
+ out.push_str(&line[frag.indent..]);
+ } else {
+ out.push_str(line);
+ }
+ out.push('\n');
+ }
+}
+
+/// Extracts the first `Cargo` fenced code block from a chunk of Markdown.
+fn extract_manifest(comment: &str) -> CargoResult<Option<String>> {
+ use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
+
+ // To match librustdoc/html/markdown.rs, opts.
+ let exts = Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES;
+
+ let md = Parser::new_ext(comment, exts);
+
+ let mut inside = false;
+ let mut output = None;
+
+ for item in md {
+ match item {
+ Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info)))
+ if info.to_lowercase() == "cargo" =>
+ {
+ if output.is_some() {
+ anyhow::bail!("multiple `cargo` manifests present")
+ } else {
+ output = Some(String::new());
+ }
+ inside = true;
+ }
+ Event::Text(ref text) if inside => {
+ let s = output.get_or_insert(String::new());
+ s.push_str(text);
+ }
+ Event::End(Tag::CodeBlock(_)) if inside => {
+ inside = false;
+ }
+ _ => (),
+ }
+ }
+
+ Ok(output)
+}
+
+#[cfg(test)]
+mod test_expand {
+ use super::*;
+
+ macro_rules! si {
+ ($i:expr) => {{
+ expand_manifest(
+ $i,
+ std::path::Path::new("/home/me/test.rs"),
+ &Config::default().unwrap(),
+ )
+ .unwrap_or_else(|err| panic!("{}", err))
+ }};
+ }
+
+ #[test]
+ fn test_default() {
+ snapbox::assert_eq(
+ r#"[[bin]]
+name = "test-"
+path = "test.rs"
+
+[package]
+autobenches = false
+autobins = false
+autoexamples = false
+autotests = false
+build = false
+edition = "2021"
+name = "test-"
+publish = false
+version = "0.0.0"
+
+[profile.release]
+strip = true
+
+[workspace]
+"#,
+ si!(r#"fn main() {}"#),
+ );
+ }
+
+ #[test]
+ fn test_dependencies() {
+ snapbox::assert_eq(
+ r#"[[bin]]
+name = "test-"
+path = "test.rs"
+
+[dependencies]
+time = "0.1.25"
+
+[package]
+autobenches = false
+autobins = false
+autoexamples = false
+autotests = false
+build = false
+edition = "2021"
+name = "test-"
+publish = false
+version = "0.0.0"
+
+[profile.release]
+strip = true
+
+[workspace]
+"#,
+ si!(r#"
+//! ```cargo
+//! [dependencies]
+//! time="0.1.25"
+//! ```
+fn main() {}
+"#),
+ );
+ }
+}
+
+#[cfg(test)]
+mod test_comment {
+ use super::*;
+
+ macro_rules! ec {
+ ($s:expr) => {
+ extract_comment($s).unwrap_or_else(|err| panic!("{}", err))
+ };
+ }
+
+ #[test]
+ fn test_no_comment() {
+ snapbox::assert_eq(
+ "no doc-comment found",
+ extract_comment(
+ r#"
+fn main () {
+}
+"#,
+ )
+ .unwrap_err()
+ .to_string(),
+ );
+ }
+
+ #[test]
+ fn test_no_comment_she_bang() {
+ snapbox::assert_eq(
+ "no doc-comment found",
+ extract_comment(
+ r#"#!/usr/bin/env cargo-eval
+
+fn main () {
+}
+"#,
+ )
+ .unwrap_err()
+ .to_string(),
+ );
+ }
+
+ #[test]
+ fn test_comment() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"//! Here is a manifest:
+//!
+//! ```cargo
+//! [dependencies]
+//! time = "*"
+//! ```
+fn main() {}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_comment_shebang() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"#!/usr/bin/env cargo-eval
+
+//! Here is a manifest:
+//!
+//! ```cargo
+//! [dependencies]
+//! time = "*"
+//! ```
+fn main() {}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_multiline_comment() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"/*!
+Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+*/
+
+fn main() {
+}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_multiline_comment_shebang() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"#!/usr/bin/env cargo-eval
+
+/*!
+Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+*/
+
+fn main() {
+}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_multiline_block_comment() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"/*!
+ * Here is a manifest:
+ *
+ * ```cargo
+ * [dependencies]
+ * time = "*"
+ * ```
+ */
+fn main() {}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_multiline_block_comment_shebang() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"#!/usr/bin/env cargo-eval
+
+/*!
+ * Here is a manifest:
+ *
+ * ```cargo
+ * [dependencies]
+ * time = "*"
+ * ```
+ */
+fn main() {}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_adjacent_comments() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r#"#!/usr/bin/env cargo-eval
+
+// I am a normal comment
+//! Here is a manifest:
+//!
+//! ```cargo
+//! [dependencies]
+//! time = "*"
+//! ```
+
+fn main () {
+}
+"#),
+ );
+ }
+
+ #[test]
+ fn test_doc_attrib() {
+ snapbox::assert_eq(
+ r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#,
+ ec!(r###"#!/usr/bin/env cargo-eval
+
+#![doc = r#"Here is a manifest:
+
+```cargo
+[dependencies]
+time = "*"
+```
+"#]
+
+fn main () {
+}
+"###),
+ );
+ }
+}
+
+#[cfg(test)]
+mod test_manifest {
+ use super::*;
+
+ macro_rules! smm {
+ ($c:expr) => {
+ extract_manifest($c)
+ };
+ }
+
+ #[test]
+ fn test_no_code_fence() {
+ assert_eq!(
+ smm!(
+ r#"There is no manifest in this comment.
+"#
+ )
+ .unwrap(),
+ None
+ );
+ }
+
+ #[test]
+ fn test_no_cargo_code_fence() {
+ assert_eq!(
+ smm!(
+ r#"There is no manifest in this comment.
+
+```
+This is not a manifest.
+```
+
+```rust
+println!("Nor is this.");
+```
+
+ Or this.
+"#
+ )
+ .unwrap(),
+ None
+ );
+ }
+
+ #[test]
+ fn test_cargo_code_fence() {
+ assert_eq!(
+ smm!(
+ r#"This is a manifest:
+
+```cargo
+dependencies = { time = "*" }
+```
+"#
+ )
+ .unwrap(),
+ Some(
+ r#"dependencies = { time = "*" }
+"#
+ .into()
+ )
+ );
+ }
+
+ #[test]
+ fn test_mixed_code_fence() {
+ assert_eq!(
+ smm!(
+ r#"This is *not* a manifest:
+
+```
+He's lying, I'm *totally* a manifest!
+```
+
+This *is*:
+
+```cargo
+dependencies = { time = "*" }
+```
+"#
+ )
+ .unwrap(),
+ Some(
+ r#"dependencies = { time = "*" }
+"#
+ .into()
+ )
+ );
+ }
+
+ #[test]
+ fn test_two_cargo_code_fence() {
+ assert!(smm!(
+ r#"This is a manifest:
+
+```cargo
+dependencies = { time = "*" }
+```
+
+So is this, but it doesn't count:
+
+```cargo
+dependencies = { explode = true }
+```
+"#
+ )
+ .is_err());
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs
index 2c213b7f5..2202f6b3b 100644
--- a/src/tools/cargo/src/cargo/util/toml/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml/mod.rs
@@ -1,4 +1,5 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
+use std::ffi::OsStr;
use std::fmt::{self, Display, Write};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
@@ -33,6 +34,7 @@ use crate::util::{
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, VersionReqExt,
};
+pub mod embedded;
mod targets;
use self::targets::targets;
@@ -54,13 +56,32 @@ pub fn read_manifest(
path.display(),
source_id
);
- let contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
+ let mut contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
+ let embedded = is_embedded(path);
+ if embedded {
+ if !config.cli_unstable().script {
+ return Err(ManifestError::new(
+ anyhow::anyhow!("parsing `{}` requires `-Zscript`", path.display()),
+ path.into(),
+ ));
+ }
+ contents = embedded::expand_manifest(&contents, path, config)
+ .map_err(|err| ManifestError::new(err, path.into()))?;
+ }
- read_manifest_from_str(&contents, path, source_id, config)
+ read_manifest_from_str(&contents, path, embedded, source_id, config)
.with_context(|| format!("failed to parse manifest at `{}`", path.display()))
.map_err(|err| ManifestError::new(err, path.into()))
}
+/// See also `bin/cargo/commands/run.rs`s `is_manifest_command`
+pub fn is_embedded(path: &Path) -> bool {
+ let ext = path.extension();
+ ext == Some(OsStr::new("rs")) ||
+ // Provide better errors by not considering directories to be embedded manifests
+ (ext.is_none() && path.is_file())
+}
+
/// Parse an already-loaded `Cargo.toml` as a Cargo manifest.
///
/// This could result in a real or virtual manifest being returned.
@@ -69,9 +90,10 @@ pub fn read_manifest(
/// within the manifest. For virtual manifests, these paths can only
/// come from patched or replaced dependencies. These paths are not
/// canonicalized.
-pub fn read_manifest_from_str(
+fn read_manifest_from_str(
contents: &str,
manifest_file: &Path,
+ embedded: bool,
source_id: SourceId,
config: &Config,
) -> CargoResult<(EitherManifest, Vec<PathBuf>)> {
@@ -127,7 +149,7 @@ pub fn read_manifest_from_str(
}
return if manifest.project.is_some() || manifest.package.is_some() {
let (mut manifest, paths) =
- TomlManifest::to_real_manifest(&manifest, source_id, package_root, config)?;
+ 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!(
@@ -1573,178 +1595,89 @@ pub struct InheritableFields {
ws_root: PathBuf,
}
-impl InheritableFields {
- 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;
- }
-
- pub fn dependencies(&self) -> CargoResult<BTreeMap<String, TomlDependency>> {
- self.dependencies.clone().map_or(
- Err(anyhow!("`workspace.dependencies` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn lints(&self) -> CargoResult<TomlLints> {
- self.lints
- .clone()
- .map_or(Err(anyhow!("`workspace.lints` was not defined")), |d| Ok(d))
- }
+/// 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 -> String),
+ ("package.version", version -> semver::Version),
+ }
+
+ /// Gets a workspace dependency with the `name`.
pub fn get_dependency(&self, name: &str, package_root: &Path) -> CargoResult<TomlDependency> {
- self.dependencies.clone().map_or(
- Err(anyhow!("`workspace.dependencies` was not defined")),
- |deps| {
- deps.get(name).map_or(
- Err(anyhow!(
- "`dependency.{}` was not found in `workspace.dependencies`",
- name
- )),
- |dep| {
- let mut dep = dep.clone();
- if let TomlDependency::Detailed(detailed) = &mut dep {
- detailed.resolve_path(name, self.ws_root(), package_root)?
- }
- Ok(dep)
- },
- )
- },
- )
- }
-
- pub fn version(&self) -> CargoResult<semver::Version> {
- self.version.clone().map_or(
- Err(anyhow!("`workspace.package.version` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn authors(&self) -> CargoResult<Vec<String>> {
- self.authors.clone().map_or(
- Err(anyhow!("`workspace.package.authors` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn description(&self) -> CargoResult<String> {
- self.description.clone().map_or(
- Err(anyhow!("`workspace.package.description` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn homepage(&self) -> CargoResult<String> {
- self.homepage.clone().map_or(
- Err(anyhow!("`workspace.package.homepage` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn documentation(&self) -> CargoResult<String> {
- self.documentation.clone().map_or(
- Err(anyhow!("`workspace.package.documentation` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
- readme_for_package(self.ws_root.as_path(), self.readme.clone()).map_or(
- Err(anyhow!("`workspace.package.readme` was not defined")),
- |readme| {
- let rel_path =
- resolve_relative_path("readme", &self.ws_root, package_root, &readme)?;
- Ok(StringOrBool::String(rel_path))
- },
- )
- }
-
- pub fn keywords(&self) -> CargoResult<Vec<String>> {
- self.keywords.clone().map_or(
- Err(anyhow!("`workspace.package.keywords` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn categories(&self) -> CargoResult<Vec<String>> {
- self.categories.clone().map_or(
- Err(anyhow!("`workspace.package.categories` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn license(&self) -> CargoResult<String> {
- self.license.clone().map_or(
- Err(anyhow!("`workspace.package.license` was not defined")),
- |d| Ok(d),
- )
+ 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> {
- self.license_file.clone().map_or(
- Err(anyhow!("`workspace.package.license_file` was not defined")),
- |d| resolve_relative_path("license-file", &self.ws_root, package_root, &d),
- )
- }
-
- pub fn repository(&self) -> CargoResult<String> {
- self.repository.clone().map_or(
- Err(anyhow!("`workspace.package.repository` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn publish(&self) -> CargoResult<VecStringOrBool> {
- self.publish.clone().map_or(
- Err(anyhow!("`workspace.package.publish` was not defined")),
- |d| Ok(d),
- )
- }
-
- pub fn edition(&self) -> CargoResult<String> {
- self.edition.clone().map_or(
- Err(anyhow!("`workspace.package.edition` was not defined")),
- |d| Ok(d),
- )
+ 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)
}
- pub fn rust_version(&self) -> CargoResult<String> {
- self.rust_version.clone().map_or(
- Err(anyhow!("`workspace.package.rust-version` was not defined")),
- |d| Ok(d),
- )
+ /// 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 badges(&self) -> CargoResult<BTreeMap<String, BTreeMap<String, String>>> {
- self.badges.clone().map_or(
- Err(anyhow!("`workspace.package.badges` was not defined")),
- |d| Ok(d),
- )
+ pub fn ws_root(&self) -> &PathBuf {
+ &self.ws_root
}
- pub fn exclude(&self) -> CargoResult<Vec<String>> {
- self.exclude.clone().map_or(
- Err(anyhow!("`workspace.package.exclude` was not defined")),
- |d| Ok(d),
- )
+ pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) {
+ self.dependencies = deps;
}
- pub fn include(&self) -> CargoResult<Vec<String>> {
- self.include.clone().map_or(
- Err(anyhow!("`workspace.package.include` was not defined")),
- |d| Ok(d),
- )
+ pub fn update_lints(&mut self, lints: Option<TomlLints>) {
+ self.lints = lints;
}
- pub fn ws_root(&self) -> &PathBuf {
- &self.ws_root
+ pub fn update_ws_path(&mut self, ws_root: PathBuf) {
+ self.ws_root = ws_root;
}
}
@@ -1975,6 +1908,7 @@ impl TomlManifest {
pub fn to_real_manifest(
me: &Rc<TomlManifest>,
+ embedded: bool,
source_id: SourceId,
package_root: &Path,
config: &Config,
@@ -2412,7 +2346,6 @@ impl TomlManifest {
let empty_features = BTreeMap::new();
let summary = Summary::new(
- config,
pkgid,
deps,
me.features.as_ref().unwrap_or(&empty_features),
@@ -2442,7 +2375,8 @@ impl TomlManifest {
.readme
.clone()
.map(|mw| mw.resolve("readme", || inherit()?.readme(package_root)))
- .transpose()?,
+ .transpose()?
+ .as_ref(),
),
authors: package
.authors
@@ -2643,6 +2577,7 @@ impl TomlManifest {
package.metabuild.clone().map(|sov| sov.0),
resolve_behavior,
rustflags,
+ embedded,
);
if package.license_file.is_some() && package.license.is_some() {
manifest.warnings_mut().add_warning(
@@ -3039,7 +2974,7 @@ 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> {
+pub fn readme_for_package(package_root: &Path, readme: Option<&StringOrBool>) -> Option<String> {
match &readme {
None => default_readme_from_package_root(package_root),
Some(value) => match value {
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 f3fc150e1..5529b8029 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
@@ -496,6 +496,11 @@ fn fix_feature_activations(
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();
+ }
if status == DependencyStatus::Required {
for value in feature_values.iter_mut() {
@@ -511,13 +516,13 @@ fn fix_feature_activations(
} = parsed_value
{
if dep_name == dep_key && weak {
- *value = format!("{dep_name}/{dep_feature}").into();
+ let mut new_value = toml_edit::Value::from(format!("{dep_name}/{dep_feature}"));
+ *new_value.decor_mut() = value.decor().clone();
+ *value = new_value;
}
}
}
}
-
- feature_values.fmt();
}
pub fn str_or_1_len_table(item: &toml_edit::Item) -> bool {
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 63f07ca55..c9dae918c 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/index.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/index.md
@@ -90,7 +90,7 @@ implemented.
The Cargo project uses several bots:
* [GitHub Actions] are used to automatically run all tests for each PR.
-* [triagebot] automatically assigns reviewers for PRs, see [Assignment] for
+* [triagebot] automatically assigns reviewers for PRs, see [PR Assignment] for
how to configure.
* [bors] is used to merge PRs. See [The merging process].
* [triagebot] is used for assigning issues to non-members, see [Issue
@@ -100,20 +100,20 @@ The Cargo project uses several bots:
[bors]: https://buildbot2.rust-lang.org/homu/
[The merging process]: working-on-cargo.md#the-merging-process
[GitHub Actions]: https://github.com/features/actions
-[triagebot]: https://github.com/rust-lang/triagebot/wiki
+[triagebot]: https://forge.rust-lang.org/triagebot/index.html
[rfcbot]: https://github.com/rust-lang/rfcbot-rs
-[Assignment]: https://github.com/rust-lang/triagebot/wiki/Assignment
+[PR Assignment]: https://forge.rust-lang.org/triagebot/pr-assignment.html
## Issue assignment
Normally, if you plan to work on an issue that has been marked with the
[S-accepted] label, it is sufficient just to leave a comment that you are
working on it. We also have a bot that allows you to formally claim an issue
-by entering the text `@rustbot claim` in a comment. See the [Assignment] docs
+by entering the text `@rustbot claim` in a comment. See the [Issue Assignment] docs
on how this works.
-[Assignment]: https://github.com/rust-lang/triagebot/wiki/Assignment
+[Issue Assignment]: https://forge.rust-lang.org/triagebot/issue-assignment.html
[team]: https://www.rust-lang.org/governance/teams/dev-tools#cargo
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo
[issue-feature-request]: https://github.com/rust-lang/cargo/labels/C-feature-request
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 e90bb8588..1567197c4 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
@@ -135,7 +135,7 @@ More information about these commands can be found at the [shortcuts documentati
[`S-waiting-on-review`]: https://github.com/rust-lang/cargo/labels/S-waiting-on-review
[`S-waiting-on-author`]: https://github.com/rust-lang/cargo/labels/S-waiting-on-author
[`@rustbot`]: https://github.com/rustbot
-[shortcuts documentation]: https://github.com/rust-lang/triagebot/wiki/Shortcuts
+[shortcuts documentation]: https://forge.rust-lang.org/triagebot/shortcuts.html
## The merging process
diff --git a/src/tools/cargo/src/doc/man/cargo-install.md b/src/tools/cargo/src/doc/man/cargo-install.md
index 385ed27c2..e6786318f 100644
--- a/src/tools/cargo/src/doc/man/cargo-install.md
+++ b/src/tools/cargo/src/doc/man/cargo-install.md
@@ -18,7 +18,7 @@ cargo-install --- Build and install a Rust binary
This command manages Cargo's local set of installed binary crates. Only
packages which have executable `[[bin]]` or `[[example]]` targets can be
installed, and all executables are installed into the installation root's
-`bin` folder.
+`bin` folder. By default only binaries, not examples, are installed.
{{> description-install-root }}
@@ -141,7 +141,7 @@ Install only the specified binary.
{{/option}}
{{#option "`--bins`" }}
-Install all binaries.
+Install all binaries. This is the default behavior.
{{/option}}
{{#option "`--example` _name_..." }}
diff --git a/src/tools/cargo/src/doc/man/cargo-test.md b/src/tools/cargo/src/doc/man/cargo-test.md
index 49792d6be..75bf72b30 100644
--- a/src/tools/cargo/src/doc/man/cargo-test.md
+++ b/src/tools/cargo/src/doc/man/cargo-test.md
@@ -59,12 +59,19 @@ on writing doc tests.
### Working directory of tests
-The working directory of every test is set to the root directory of the package
-the test belongs to.
-Setting the working directory of tests to the package's root directory makes it
+The working directory when running each unit and integration test is set to the
+root directory of the package the test belongs to.
+Setting the working directory of tests to the package's root directory makes it
possible for tests to reliably access the package's files using relative paths,
regardless from where `cargo test` was executed from.
+For documentation tests, the working directory when invoking `rustdoc` is set to
+the workspace root directory, and is also the directory `rustdoc` uses as the
+compilation directory of each documentation test.
+The working directory when running each documentation test is set to the root
+directory of the package the test belongs to, and is controlled via `rustdoc`'s
+`--test-run-directory` option.
+
## OPTIONS
### Test 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 9108d6165..9610b72f0 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
@@ -410,6 +410,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 ff8bdb5ba..e396410d3 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
@@ -341,6 +341,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 bf8cb48f3..9814f445d 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
@@ -326,6 +326,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 825032826..09f9b68c9 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
@@ -297,6 +297,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 87d72ad38..b83c360eb 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
@@ -399,6 +399,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 7aefd3492..a5b696111 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
@@ -13,7 +13,8 @@ DESCRIPTION
This command manages Cargo’s local set of installed binary crates.
Only packages which have executable [[bin]] or [[example]] targets can
be installed, and all executables are installed into the installation
- root’s bin folder.
+ root’s bin folder. By default only binaries, not examples, are
+ installed.
The installation root is determined, in order of precedence:
@@ -137,7 +138,7 @@ OPTIONS
Install only the specified binary.
--bins
- Install all binaries.
+ Install all binaries. This is the default behavior.
--example name…
Install only the specified example.
@@ -273,6 +274,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt
index 960e0248e..1201f3d07 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt
@@ -191,6 +191,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt
index d35172ad7..e8850e3d4 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt
@@ -157,6 +157,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 f6782be11..5fcfe66b4 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
@@ -245,6 +245,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 cc4241f93..7a70c5363 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
@@ -343,6 +343,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 6a32a6b6e..4db66b3a8 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
@@ -313,6 +313,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
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 9e26f8aae..7955b0e3d 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
@@ -53,11 +53,18 @@ DESCRIPTION
information on writing doc tests.
Working directory of tests
- The working directory of every test is set to the root directory of the
- package the test belongs to. Setting the working directory of tests to
- the package’s root directory makes it possible for tests to reliably
- access the package’s files using relative paths, regardless from where
- cargo test was executed from.
+ The working directory when running each unit and integration test is set
+ to the root directory of the package the test belongs to. Setting the
+ working directory of tests to the package’s root directory makes it
+ possible for tests to reliably access the package’s files using
+ relative paths, regardless from where cargo test was executed from.
+
+ For documentation tests, the working directory when invoking rustdoc is
+ set to the workspace root directory, and is also the directory rustdoc
+ uses as the compilation directory of each documentation test. The
+ working directory when running each documentation test is set to the
+ root directory of the package the test belongs to, and is controlled via
+ rustdoc’s --test-run-directory option.
OPTIONS
Test Options
@@ -432,6 +439,7 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number
of parallel jobs to the number of logical CPUs plus provided value.
+ If a string default is provided, it sets the value back to defaults.
Should not be 0.
--keep-going
diff --git a/src/tools/cargo/src/doc/man/includes/options-jobs.md b/src/tools/cargo/src/doc/man/includes/options-jobs.md
index 274263866..0030e18d8 100644
--- a/src/tools/cargo/src/doc/man/includes/options-jobs.md
+++ b/src/tools/cargo/src/doc/man/includes/options-jobs.md
@@ -2,6 +2,7 @@
Number of parallel jobs to run. May also be specified with the
`build.jobs` [config value](../reference/config.html). Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string `default` is provided, it sets the value back to defaults.
Should not be 0.
{{/option}}
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 df0101be5..70ae187a7 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-bench.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-bench.md
@@ -476,7 +476,8 @@ Rust test harness runs benchmarks serially in a single thread.
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 6e3cf157a..525ab14e5 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-build.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-build.md
@@ -402,7 +402,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 2070293ac..4dbc97ad8 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-check.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-check.md
@@ -383,7 +383,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 e0e5c8ed2..d68bb84e7 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-doc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-doc.md
@@ -357,7 +357,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 1b9ec6a85..2bf83fc2e 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-fix.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-fix.md
@@ -463,7 +463,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
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 5d413010c..af8efcae8 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-install.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-install.md
@@ -18,7 +18,7 @@ cargo-install --- Build and install a Rust binary
This command manages Cargo's local set of installed binary crates. Only
packages which have executable `[[bin]]` or `[[example]]` targets can be
installed, and all executables are installed into the installation root's
-`bin` folder.
+`bin` folder. By default only binaries, not examples, are installed.
The installation root is determined, in order of precedence:
@@ -150,7 +150,7 @@ same time.</dd>
<dt class="option-term" id="option-cargo-install---bins"><a class="option-anchor" href="#option-cargo-install---bins"></a><code>--bins</code></dt>
-<dd class="option-desc">Install all binaries.</dd>
+<dd class="option-desc">Install all binaries. This is the default behavior.</dd>
<dt class="option-term" id="option-cargo-install---example"><a class="option-anchor" href="#option-cargo-install---example"></a><code>--example</code> <em>name</em>…</dt>
@@ -312,7 +312,8 @@ offline.</p>
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-package.md b/src/tools/cargo/src/doc/src/commands/cargo-package.md
index 776b150cf..f9c7c552d 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-package.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-package.md
@@ -227,7 +227,8 @@ offline.</p>
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-publish.md b/src/tools/cargo/src/doc/src/commands/cargo-publish.md
index 1f4fbebb8..fe37d9f97 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-publish.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-publish.md
@@ -193,7 +193,8 @@ offline.</p>
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
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 f6f5ec2a3..bd3e4724a 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-run.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-run.md
@@ -299,7 +299,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 946298af9..2147d617c 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
@@ -396,7 +396,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
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 8467da2a3..22c1c8322 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
@@ -376,7 +376,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</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 24fcc70ff..e38e9929e 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-test.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-test.md
@@ -59,12 +59,19 @@ on writing doc tests.
### Working directory of tests
-The working directory of every test is set to the root directory of the package
-the test belongs to.
-Setting the working directory of tests to the package's root directory makes it
+The working directory when running each unit and integration test is set to the
+root directory of the package the test belongs to.
+Setting the working directory of tests to the package's root directory makes it
possible for tests to reliably access the package's files using relative paths,
regardless from where `cargo test` was executed from.
+For documentation tests, the working directory when invoking `rustdoc` is set to
+the workspace root directory, and is also the directory `rustdoc` uses as the
+compilation directory of each documentation test.
+The working directory when running each documentation test is set to the root
+directory of the package the test belongs to, and is controlled via `rustdoc`'s
+`--test-run-directory` option.
+
## OPTIONS
### Test Options
@@ -503,7 +510,8 @@ includes an option to control the number of threads used:
<dd class="option-desc">Number of parallel jobs to run. May also be specified with the
<code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
diff --git a/src/tools/cargo/src/doc/src/faq.md b/src/tools/cargo/src/doc/src/faq.md
index abe90c000..e4a753413 100644
--- a/src/tools/cargo/src/doc/src/faq.md
+++ b/src/tools/cargo/src/doc/src/faq.md
@@ -259,3 +259,49 @@ Some issues we've seen historically which can cause crates to get rebuilt are:
If after trying to debug your issue, however, you're still running into problems
then feel free to [open an
issue](https://github.com/rust-lang/cargo/issues/new)!
+
+### What does "version conflict" mean and how to resolve it?
+
+> failed to select a version for `x` which could resolve this conflict
+
+Have you seen the error message above?
+
+This is one of the most annoying error message for Cargo users. There are several
+situations may lead us to a version conflict. Below we'll walk through possible
+causes and provide diagnostic techniques to help you out there:
+
+- The project and its dependencies use [links] to repeatedly link the local
+ library. Cargo forbids linking two packages with the same native library, so
+ even with multiple layers of dependencies it is not allowed. In this case, the
+ error message will prompt: `Only one package in the dependency graph may specify
+ the same links value`, you may need to manually check and delete duplicate link
+ values. The community also have [conventions in place] to alleviate this.
+
+- When depending on different crates in the project, if these crates use the same
+ dependent library, but the version used is restricted, making it impossible to
+ determine the correct version, it will also cause conflicts. The error message
+ will prompt: `all possible versions conflict with previously selected packages`.
+ You may need to modify the version requirements to make them consistent.
+
+- If there are multiple versions of dependencies in the project, when using
+ [`direct-minimal-versions`], the minimum version requirements cannot be met,
+ which will cause conflicts. You may need to modify version requirements of your
+ direct dependencies to meet the minimum SemVer version accordingly.
+
+- If the dependent crate does not have the features you choose, it will also
+ cause conflicts. At this time, you need to check the dependent version and its
+ features.
+
+- Conflicts may occur when merging branches or PRs, if there are non-trivial
+ conflicts, you can reset all "yours" changes, fix all other conflicts in the
+ branch, and then run some cargo command (like `cargo tree` or `cargo check`),
+ which should re-update the lockfile with your own local changes. If you previously
+ ran some `cargo update` commands in your branch, you can re-run them that this
+ time. The community has been looking to resolve merge conflicts with `Cargo.lock`
+ and `Cargo.toml` using a [custom merge tool].
+
+
+[links]: https://doc.rust-lang.org/cargo/reference/resolver.html#links
+[conventions in place]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages
+[`direct-minimal-versions`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#direct-minimal-versions
+[custom merge tool]: https://github.com/rust-lang/cargo/issues/1818
diff --git a/src/tools/cargo/src/doc/src/reference/config.md b/src/tools/cargo/src/doc/src/reference/config.md
index c57a45f67..30053bb18 100644
--- a/src/tools/cargo/src/doc/src/reference/config.md
+++ b/src/tools/cargo/src/doc/src/reference/config.md
@@ -370,13 +370,14 @@ recursive_example = "rr --example recursions"
The `[build]` table controls build-time operations and compiler settings.
##### `build.jobs`
-* Type: integer
+* Type: integer or string
* Default: number of logical CPUs
* Environment: `CARGO_BUILD_JOBS`
Sets the maximum number of compiler processes to run in parallel. If negative,
it sets the maximum number of compiler processes to the number of logical CPUs
-plus provided value. Should not be 0.
+plus provided value. Should not be 0. If a string `default` is provided, it sets
+the value back to defaults.
Can be overridden with the `--jobs` CLI option.
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 95c4c87fb..353742877 100644
--- a/src/tools/cargo/src/doc/src/reference/environment-variables.md
+++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md
@@ -274,6 +274,7 @@ on the platform:
* Windows: `PATH`
* macOS: `DYLD_FALLBACK_LIBRARY_PATH`
* Unix: `LD_LIBRARY_PATH`
+* AIX: `LIBPATH`
The value is extended from the existing value when Cargo starts. macOS has
special consideration where if `DYLD_FALLBACK_LIBRARY_PATH` is not already
diff --git a/src/tools/cargo/src/doc/src/reference/registry-index.md b/src/tools/cargo/src/doc/src/reference/registry-index.md
index 9e5be3e5b..3eed6ef39 100644
--- a/src/tools/cargo/src/doc/src/reference/registry-index.md
+++ b/src/tools/cargo/src/doc/src/reference/registry-index.md
@@ -80,6 +80,8 @@ harder to support older versions of Cargo that lack `{prefix}`/`{lowerprefix}`.
For example, nginx rewrite rules can easily construct `{prefix}` but can't
perform case-conversion to construct `{lowerprefix}`.
+### Name restrictions
+
Registries should consider enforcing limitations on package names added to
their index. Cargo itself allows names with any [alphanumeric], `-`, or `_`
characters. [crates.io] imposes its own limitations, including the following:
@@ -98,6 +100,14 @@ attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack) and other
concerns in [UTR36](https://www.unicode.org/reports/tr36/) and
[UTS39](https://www.unicode.org/reports/tr39/).
+### Version uniqueness
+
+Indexes *must* ensure that each version only appears once for each package.
+This includes ignoring SemVer build metadata.
+For example, the index must *not* contain two entries with a version `1.0.7` and `1.0.7+extra`.
+
+### JSON schema
+
Each line in a package file contains a JSON object that describes a published
version of the package. The following is a pretty-printed example with comments
explaining the format of the entry.
diff --git a/src/tools/cargo/src/doc/src/reference/resolver.md b/src/tools/cargo/src/doc/src/reference/resolver.md
index af02d6cf2..151648f43 100644
--- a/src/tools/cargo/src/doc/src/reference/resolver.md
+++ b/src/tools/cargo/src/doc/src/reference/resolver.md
@@ -50,7 +50,7 @@ Tilde | `~1.2` | <code>>=1.2.0,&nbsp;<1.3.0</code> | Minimum version, with restr
Wildcard | `1.*` | <code>>=1.0.0,&nbsp;<2.0.0</code> | Any version in the `*` position.
Equals | `=1.2.3` | <code>=1.2.3</code> | Exactly the specified version only.
Comparison | `>1.1` | <code>>=1.2.0</code> | Naive numeric comparison of specified digits.
-Compound | <code>>=1.2,&nbsp;<1.5</code> | <code>>1.2.0,&nbsp;<1.5.0</code> | Multiple requirements that must be simultaneously satisfied.
+Compound | <code>>=1.2,&nbsp;<1.5</code> | <code>>=1.2.0,&nbsp;<1.5.0</code> | Multiple requirements that must be simultaneously satisfied.
When multiple packages specify a dependency for a common package, the resolver
attempts to ensure that they use the same version of that common package, as
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 8d9eac308..8941e67ad 100644
--- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
@@ -107,6 +107,43 @@ Here are some examples of comparison requirements:
As shown in the examples above, multiple version requirements can be
separated with a comma, e.g., `>= 1.2, < 1.5`.
+> **Recommendation:** When in doubt, use the default version requirement operator.
+>
+> In rare circumstances, a package with a "public dependency"
+> (re-exports the dependency or interoperates with it in its public API)
+> that is compatible with multiple semver-incompatible versions
+> (e.g. only uses a simple type that hasn't changed between releases, like an `Id`)
+> may support users choosing which version of the "public dependency" to use.
+> In this case, a version requirement like `">=0.4, <2"` may be of interest.
+> *However* users of the package will likely run into errors and need to to
+> manually select a version of the "public dependency" via `cargo update` if
+> they also depend on it as Cargo might pick different versions of the "public
+> dependency" when [resolving dependency versions](resolver.md) (see
+> [#10599]).
+>
+> 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).
+> Consider whether controlling the version in your [`Cargo.lock`] would be more
+> appropriate.
+>
+> In some instances this won't matter or the benefits might outweigh the cost, including:
+> - When no one else depends on your package e.g. it only has a `[[bin]]`
+> - When depending on a pre-release package and wishing to avoid breaking
+> changes then a fully specified `"=1.2.3-alpha.3"` might be warranted (see
+> [#2222])
+> - When a library re-exports a proc-macro but the proc-macro generates code that
+> calls into the re-exporting library then a fully specified `=1.2.3` might be
+> warranted to ensure the proc-macro isn't newer than the re-exporting library
+> and generating code that uses parts of the API that don't exist within the
+> current version
+
+[`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
+[#10599]: https://github.com/rust-lang/cargo/issues/10599
+
### Specifying dependencies from other registries
To specify a dependency from a registry other than [crates.io], first the
@@ -140,7 +177,8 @@ Cargo will fetch the `git` repository at this location then look for a
of a workspace and setting `git` to the repository containing the workspace).
Since we haven’t specified any other information, Cargo assumes that
-we intend to use the latest commit on the main branch to build our package.
+we intend to use the latest commit on the default branch branch
+to build our package, which may not necessarily be the main branch.
You can combine the `git` key with the `rev`, `tag`, or `branch` keys to
specify something else. Here's an example of specifying that you want to use
the latest commit on a branch named `next`:
diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md
index 7484db9b5..b59319196 100644
--- a/src/tools/cargo/src/doc/src/reference/unstable.md
+++ b/src/tools/cargo/src/doc/src/reference/unstable.md
@@ -52,10 +52,12 @@ how the feature works:
```
Each new feature described below should explain how to use it.
+For the latest nightly, see the [nightly version] of this page.
[config file]: config.md
[nightly channel]: ../../book/appendix-07-nightly-rust.html
[stabilized]: https://doc.crates.io/contrib/process/unstable.html#stabilization
+[nightly version]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#script
### List of unstable features
@@ -85,7 +87,6 @@ Each new feature described below should explain how to use it.
* [host-config](#host-config) --- Allows setting `[target]`-like configuration settings for host build targets.
* [target-applies-to-host](#target-applies-to-host) --- Alters whether certain flags will be passed to host build targets.
* rustdoc
- * [`doctest-in-workspace`](#doctest-in-workspace) --- Fixes workspace-relative paths when running doctests.
* [rustdoc-map](#rustdoc-map) --- Provides mappings for documentation to link to external sites like [docs.rs](https://docs.rs/).
* [scrape-examples](#scrape-examples) --- Shows examples within documentation.
* `Cargo.toml` extensions
@@ -107,6 +108,7 @@ Each new feature described below should explain how to use it.
* [registry-auth](#registry-auth) --- Adds support for authenticated registries, and generate registry authentication tokens using asymmetric cryptography.
* Other
* [gitoxide](#gitoxide) --- Use `gitoxide` instead of `git2` for a set of operations.
+ * [script](#script) --- Enable support for single-file `.rs` packages.
### allow-features
@@ -450,27 +452,30 @@ cargo check --keep-going -Z unstable-options
### config-include
* Tracking Issue: [#7723](https://github.com/rust-lang/cargo/issues/7723)
+This feature requires the `-Zconfig-include` command-line option.
+
The `include` key in a config file can be used to load another config file. It
-takes a string for a path to another file relative to the config file, or a
-list of strings. It requires the `-Zconfig-include` command-line option.
+takes a string for a path to another file relative to the config file, or an
+array of config file paths. Only path ending with `.toml` is accepted.
```toml
-# .cargo/config
-include = '../../some-common-config.toml'
-```
-
-The config values are first loaded from the include path, and then the config
-file's own values are merged on top of it.
+# a path ending with `.toml`
+include = "path/to/mordor.toml"
-This can be paired with [config-cli](#config-cli) to specify a file to load
-from the command-line. Pass a path to a config file as the argument to
-`--config`:
-
-```console
-cargo +nightly -Zunstable-options -Zconfig-include --config somefile.toml build
+# or an array of paths
+include = ["frodo.toml", "samwise.toml"]
```
-CLI paths are relative to the current working directory.
+Unlike other config values, the merge behavior of the `include` key is
+different. When a config file contains an `include` key:
+
+1. The config values are first loaded from the `include` path.
+ * If the value of the `include` key is an array of paths, the config values
+ are loaded and merged from left to right for each path.
+ * Recurse this step if the config values from the `include` path also
+ contain an `include` key.
+2. Then, the config file's own values are merged on top of the config
+ from the `include` path.
### target-applies-to-host
* Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322)
@@ -1185,24 +1190,6 @@ cargo +nightly -Zunstable-options config get build.rustflags
If no config value is included, it will display all config values. See the
`--help` output for more options available.
-### `doctest-in-workspace`
-
-* Tracking Issue: [#9427](https://github.com/rust-lang/cargo/issues/9427)
-
-The `-Z doctest-in-workspace` flag changes the behavior of the current working
-directory used when running doctests. Historically, Cargo has run `rustdoc
---test` relative to the root of the package, with paths relative from that
-root. However, this is inconsistent with how `rustc` and `rustdoc` are
-normally run in a workspace, where they are run relative to the workspace
-root. This inconsistency causes problems in various ways, such as when passing
-RUSTDOCFLAGS with relative paths, or dealing with diagnostic output.
-
-The `-Z doctest-in-workspace` flag causes cargo to switch to running `rustdoc`
-from the root of the workspace. It also passes the `--test-run-directory` to
-`rustdoc` so that when *running* the tests, they are run from the root of the
-package. This preserves backwards compatibility and is consistent with how
-normal unittests are run.
-
### rustc `--print`
* Tracking Issue: [#9357](https://github.com/rust-lang/cargo/issues/9357)
@@ -1392,6 +1379,113 @@ Valid operations are the following:
* When the unstable feature is on, fetching/cloning a git repository is always a shallow fetch. This roughly equals to `git fetch --depth 1` everywhere.
* Even with the presence of `Cargo.lock` or specifying a commit `{ rev = "…" }`, gitoxide is still smart enough to shallow fetch without unshallowing the existing repository.
+### script
+
+* Tracking Issue: [#12207](https://github.com/rust-lang/cargo/issues/12207)
+
+Cargo can directly run `.rs` files as:
+```console
+$ cargo +nightly -Zscript file.rs
+```
+where `file.rs` can be as simple as:
+```rust
+fn main() {}
+```
+
+A user may optionally specify a manifest in a `cargo` code fence in a module-level comment, like:
+```rust
+#!/usr/bin/env -S cargo +nightly -Zscript
+
+//! ```cargo
+//! [dependencies]
+//! clap = { version = "4.2", features = ["derive"] }
+//! ```
+
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[clap(version)]
+struct Args {
+ #[clap(short, long, help = "Path to config")]
+ config: Option<std::path::PathBuf>,
+}
+
+fn main() {
+ let args = Args::parse();
+ println!("{:?}", args);
+}
+```
+
+#### Single-file packages
+
+In addition to today's multi-file packages (`Cargo.toml` file with other `.rs`
+files), we are adding the concept of single-file packages which may contain an
+embedded manifest. There is no required distinguishment for a single-file
+`.rs` package from any other `.rs` file.
+
+Single-file packages may be selected via `--manifest-path`, like
+`cargo test --manifest-path foo.rs`. Unlike `Cargo.toml`, these files cannot be auto-discovered.
+
+A single-file package may contain an embedded manifest. An embedded manifest
+is stored using `TOML` in a markdown code-fence with `cargo` at the start of the
+infostring inside a target-level doc-comment. It is an error to have multiple
+`cargo` code fences in the target-level doc-comment. We can relax this later,
+either merging the code fences or ignoring later code fences.
+
+Supported forms of embedded manifest are:
+``````rust
+//! ```cargo
+//! ```
+``````
+``````rust
+/*!
+ * ```cargo
+ * ```
+ */
+``````
+
+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
+
+Disallowed manifest fields:
+- `[workspace]`, `[lib]`, `[[bin]]`, `[[example]]`, `[[test]]`, `[[bench]]`
+- `package.workspace`, `package.build`, `package.links`, `package.autobins`, `package.autoexamples`, `package.autotests`, `package.autobenches`
+
+The default `CARGO_TARGET_DIR` for single-file packages is at `$CARGO_HOME/target/<hash>`:
+- Avoid conflicts from multiple single-file packages being in the same directory
+- Avoid problems with the single-file package's parent directory being read-only
+- Avoid cluttering the user's directory
+
+The lockfile for single-file packages will be placed in `CARGO_TARGET_DIR`. In
+the future, when workspaces are supported, that will allow a user to have a
+persistent lockfile.
+
+#### Manifest-commands
+
+You may pass a manifest directly to the `cargo` command, without a subcommand,
+like `foo/Cargo.toml` or a single-file package like `foo.rs`. This is mostly
+intended for being put in `#!` lines.
+
+The precedence for how to interpret `cargo <subcommand>` is
+1. Built-in xor single-file packages
+2. Aliases
+3. External subcommands
+
+A parameter is identified as a manifest-command if it has one of:
+- Path separators
+- A `.rs` extension
+- The file name is `Cargo.toml`
+
+Differences between `cargo run --manifest-path <path>` and `cargo <path>`
+- `cargo <path>` runs with the config for `<path>` and not the current dir, more like `cargo install --path <path>`
+- `cargo <path>` is at a verbosity level below the normal default. Pass `-v` to get normal output.
+
### `[lints]`
* Tracking Issue: [#12115](https://github.com/rust-lang/cargo/issues/12115)
@@ -1693,3 +1787,11 @@ See [Registry Protocols](registries.md#registry-protocols) for more information.
The [`cargo logout`] command has been stabilized in the 1.70 release.
[target triple]: ../appendix/glossary.md#target '"target" (glossary)'
+
+
+### `doctest-in-workspace`
+
+The `-Z doctest-in-workspace` option for `cargo test` has been stabilized and
+enabled by default in the 1.72 release. See the
+[`cargo test` documentation](../commands/cargo-test.md#working-directory-of-tests)
+for more information about the working directory for compiling and running tests.
diff --git a/src/tools/cargo/src/etc/man/cargo-bench.1 b/src/tools/cargo/src/etc/man/cargo-bench.1
index 44ff593fd..993dd3415 100644
--- a/src/tools/cargo/src/etc/man/cargo-bench.1
+++ b/src/tools/cargo/src/etc/man/cargo-bench.1
@@ -497,7 +497,8 @@ Rust test harness runs benchmarks serially in a single thread.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-build.1 b/src/tools/cargo/src/etc/man/cargo-build.1
index 80ae4ac90..4ee6a0d76 100644
--- a/src/tools/cargo/src/etc/man/cargo-build.1
+++ b/src/tools/cargo/src/etc/man/cargo-build.1
@@ -412,7 +412,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-check.1 b/src/tools/cargo/src/etc/man/cargo-check.1
index cf7a66d89..1aada2a21 100644
--- a/src/tools/cargo/src/etc/man/cargo-check.1
+++ b/src/tools/cargo/src/etc/man/cargo-check.1
@@ -393,7 +393,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-doc.1 b/src/tools/cargo/src/etc/man/cargo-doc.1
index 63ce2a050..24621e9f6 100644
--- a/src/tools/cargo/src/etc/man/cargo-doc.1
+++ b/src/tools/cargo/src/etc/man/cargo-doc.1
@@ -360,7 +360,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-fix.1 b/src/tools/cargo/src/etc/man/cargo-fix.1
index 51b1e3fd6..7f2a34cda 100644
--- a/src/tools/cargo/src/etc/man/cargo-fix.1
+++ b/src/tools/cargo/src/etc/man/cargo-fix.1
@@ -488,7 +488,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-install.1 b/src/tools/cargo/src/etc/man/cargo-install.1
index a9eb6266b..917c0d0e1 100644
--- a/src/tools/cargo/src/etc/man/cargo-install.1
+++ b/src/tools/cargo/src/etc/man/cargo-install.1
@@ -17,7 +17,7 @@ cargo\-install \[em] Build and install a Rust binary
This command manages Cargo\[cq]s local set of installed binary crates. Only
packages which have executable \fB[[bin]]\fR or \fB[[example]]\fR targets can be
installed, and all executables are installed into the installation root\[cq]s
-\fBbin\fR folder.
+\fBbin\fR folder. By default only binaries, not examples, are installed.
.sp
The installation root is determined, in order of precedence:
.sp
@@ -177,7 +177,7 @@ Install only the specified binary.
.sp
\fB\-\-bins\fR
.RS 4
-Install all binaries.
+Install all binaries. This is the default behavior.
.RE
.sp
\fB\-\-example\fR \fIname\fR\[u2026]
@@ -338,7 +338,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-package.1 b/src/tools/cargo/src/etc/man/cargo-package.1
index 9f4847d7d..8a7b1c191 100644
--- a/src/tools/cargo/src/etc/man/cargo-package.1
+++ b/src/tools/cargo/src/etc/man/cargo-package.1
@@ -234,7 +234,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-publish.1 b/src/tools/cargo/src/etc/man/cargo-publish.1
index a54a7bcda..d18f9e690 100644
--- a/src/tools/cargo/src/etc/man/cargo-publish.1
+++ b/src/tools/cargo/src/etc/man/cargo-publish.1
@@ -184,7 +184,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-run.1 b/src/tools/cargo/src/etc/man/cargo-run.1
index 7a85298cc..1c182ad1a 100644
--- a/src/tools/cargo/src/etc/man/cargo-run.1
+++ b/src/tools/cargo/src/etc/man/cargo-run.1
@@ -297,7 +297,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-rustc.1 b/src/tools/cargo/src/etc/man/cargo-rustc.1
index 6e901d9ec..50df99656 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustc.1
@@ -411,7 +411,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-rustdoc.1 b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
index 0c9a0e74a..1792c6e2f 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustdoc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
@@ -379,7 +379,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details.
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/src/etc/man/cargo-test.1 b/src/tools/cargo/src/etc/man/cargo-test.1
index 1ee2f7672..802169815 100644
--- a/src/tools/cargo/src/etc/man/cargo-test.1
+++ b/src/tools/cargo/src/etc/man/cargo-test.1
@@ -54,11 +54,18 @@ and may change in the future; beware of depending on it.
See the \fIrustdoc book\fR <https://doc.rust\-lang.org/rustdoc/> for more information
on writing doc tests.
.SS "Working directory of tests"
-The working directory of every test is set to the root directory of the package
-the test belongs to.
-Setting the working directory of tests to the package\[cq]s root directory makes it
+The working directory when running each unit and integration test is set to the
+root directory of the package the test belongs to.
+Setting the working directory of tests to the package\[cq]s root directory makes it
possible for tests to reliably access the package\[cq]s files using relative paths,
regardless from where \fBcargo test\fR was executed from.
+.sp
+For documentation tests, the working directory when invoking \fBrustdoc\fR is set to
+the workspace root directory, and is also the directory \fBrustdoc\fR uses as the
+compilation directory of each documentation test.
+The working directory when running each documentation test is set to the root
+directory of the package the test belongs to, and is controlled via \fBrustdoc\fR\[cq]s
+\fB\-\-test\-run\-directory\fR option.
.SH "OPTIONS"
.SS "Test Options"
.sp
@@ -523,7 +530,8 @@ cargo test \-j 2 \-\- \-\-test\-threads=2
Number of parallel jobs to run. May also be specified with the
\fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to
the number of logical CPUs. If negative, it sets the maximum number of
-parallel jobs to the number of logical CPUs plus provided value.
+parallel jobs to the number of logical CPUs plus provided value. If
+a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
diff --git a/src/tools/cargo/tests/internal.rs b/src/tools/cargo/tests/internal.rs
deleted file mode 100644
index c42cfa8f0..000000000
--- a/src/tools/cargo/tests/internal.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-//! Tests for internal code checks.
-
-#![allow(clippy::all)]
-
-use std::fs;
-
-#[test]
-fn check_forbidden_code() {
- // Do not use certain macros, functions, etc.
- if !cargo_util::is_ci() {
- // Only check these on CI, otherwise it could be annoying.
- use std::io::Write;
- writeln!(
- std::io::stderr(),
- "\nSkipping check_forbidden_code test, set CI=1 to enable"
- )
- .unwrap();
- return;
- }
- let root_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
- for entry in walkdir::WalkDir::new(&root_path)
- .into_iter()
- .filter_entry(|e| e.path() != root_path.join("doc"))
- .filter_map(|e| e.ok())
- {
- let path = entry.path();
- if !entry
- .file_name()
- .to_str()
- .map(|s| s.ends_with(".rs"))
- .unwrap_or(false)
- {
- continue;
- }
- eprintln!("checking {}", path.display());
- let c = fs::read_to_string(path).unwrap();
- for (line_index, line) in c.lines().enumerate() {
- if line.trim().starts_with("//") {
- continue;
- }
- if line_has_print(line) {
- if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") {
- // An exception.
- continue;
- }
- panic!(
- "found print macro in {}:{}\n\n{}\n\n\
- print! macros should not be used in Cargo because they can panic.\n\
- Use one of the drop_print macros instead.\n\
- ",
- path.display(),
- line_index,
- line
- );
- }
- if line_has_macro(line, "dbg") {
- panic!(
- "found dbg! macro in {}:{}\n\n{}\n\n\
- dbg! should not be used outside of debugging.",
- path.display(),
- line_index,
- line
- );
- }
- }
- }
-}
-
-fn line_has_print(line: &str) -> bool {
- line_has_macro(line, "print")
- || line_has_macro(line, "eprint")
- || line_has_macro(line, "println")
- || line_has_macro(line, "eprintln")
-}
-
-#[test]
-fn line_has_print_works() {
- assert!(line_has_print("print!"));
- assert!(line_has_print("println!"));
- assert!(line_has_print("eprint!"));
- assert!(line_has_print("eprintln!"));
- assert!(line_has_print("(print!(\"hi!\"))"));
- assert!(!line_has_print("print"));
- assert!(!line_has_print("i like to print things"));
- assert!(!line_has_print("drop_print!"));
- assert!(!line_has_print("drop_println!"));
- assert!(!line_has_print("drop_eprint!"));
- assert!(!line_has_print("drop_eprintln!"));
-}
-
-fn line_has_macro(line: &str, mac: &str) -> bool {
- for (i, _) in line.match_indices(mac) {
- if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") {
- continue;
- }
- if i == 0 {
- return true;
- }
- // Check for identifier boundary start.
- let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap();
- if prev1.is_alphanumeric() || prev1 == '_' {
- continue;
- }
- return true;
- }
- false
-}
diff --git a/src/tools/cargo/tests/testsuite/bench.rs b/src/tools/cargo/tests/testsuite/bench.rs
index 60ad2b60d..581acbe15 100644
--- a/src/tools/cargo/tests/testsuite/bench.rs
+++ b/src/tools/cargo/tests/testsuite/bench.rs
@@ -313,9 +313,8 @@ fn cargo_bench_failing_test() {
[FINISHED] bench [optimized] target(s) in [..]
[RUNNING] [..] (target/release/deps/foo-[..][EXE])",
)
- .with_stdout_contains(
- "[..]thread '[..]' panicked at 'assertion failed: `(left == right)`[..]",
- )
+ .with_stdout_contains("[..]thread '[..]' panicked at[..]")
+ .with_stdout_contains("[..]assertion failed[..]")
.with_stdout_contains("[..]left: `\"hello\"`[..]")
.with_stdout_contains("[..]right: `\"nope\"`[..]")
.with_stdout_contains("[..]src/main.rs:15[..]")
diff --git a/src/tools/cargo/tests/testsuite/build.rs b/src/tools/cargo/tests/testsuite/build.rs
index 7b555a71b..8cb064a6f 100644
--- a/src/tools/cargo/tests/testsuite/build.rs
+++ b/src/tools/cargo/tests/testsuite/build.rs
@@ -1334,13 +1334,13 @@ fn cargo_default_env_metadata_env_var() {
[COMPILING] bar v0.0.1 ([CWD]/bar)
[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \
--emit=[..]link \
- -C prefer-dynamic[..]-C debuginfo=2 \
+ -C prefer-dynamic[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
-C extra-filename=[..] \
--out-dir [..] \
@@ -1362,13 +1362,13 @@ fn cargo_default_env_metadata_env_var() {
[COMPILING] bar v0.0.1 ([CWD]/bar)
[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \
--emit=[..]link \
- -C prefer-dynamic[..]-C debuginfo=2 \
+ -C prefer-dynamic[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
-C extra-filename=[..] \
--out-dir [..] \
@@ -2053,7 +2053,7 @@ fn verbose_build() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
@@ -4074,7 +4074,7 @@ fn message_format_json_forward_stderr() {
},
"profile":{
"debug_assertions":false,
- "debuginfo":null,
+ "debuginfo":0,
"opt_level":"3",
"overflow_checks": false,
"test":false
@@ -5432,7 +5432,7 @@ fn targets_selected_all() {
// Unit tests.
.with_stderr_contains(
"[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\
- -C debuginfo=2 --test [..]",
+ -C debuginfo=2 [..]--test [..]",
)
.run();
}
@@ -5449,7 +5449,7 @@ fn all_targets_no_lib() {
// Unit tests.
.with_stderr_contains(
"[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\
- -C debuginfo=2 --test [..]",
+ -C debuginfo=2 [..]--test [..]",
)
.run();
}
@@ -5566,6 +5566,8 @@ fn good_jobs() {
p.cargo("build --jobs 1").run();
p.cargo("build --jobs -1").run();
+
+ p.cargo("build --jobs default").run();
}
#[cargo_test]
@@ -5599,8 +5601,8 @@ fn invalid_jobs() {
.run();
p.cargo("build --jobs over9000")
- .with_status(1)
- .with_stderr("error: Invalid value: could not parse `over9000` as a number")
+ .with_status(101)
+ .with_stderr("error: could not parse `over9000`. Number of parallel jobs should be `default` or a number.")
.run();
}
@@ -5920,7 +5922,7 @@ fn build_lib_only() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs
index 80a24960e..4840356c6 100644
--- a/src/tools/cargo/tests/testsuite/build_script.rs
+++ b/src/tools/cargo/tests/testsuite/build_script.rs
@@ -1755,7 +1755,7 @@ fn build_cmd_with_a_build_cmd() {
--extern a=[..]liba[..].rlib`
[RUNNING] `[..]/foo-[..]/build-script-build`
[RUNNING] `rustc --crate-name foo [..]lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L [..]target/debug/deps`
@@ -1926,7 +1926,6 @@ fn output_separate_lines_new() {
.run();
}
-#[cfg(not(windows))] // FIXME(#867)
#[cargo_test]
fn code_generation() {
let p = project()
@@ -1976,7 +1975,7 @@ fn code_generation() {
"\
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
-[RUNNING] `target/debug/foo`",
+[RUNNING] `target/debug/foo[EXE]`",
)
.with_stdout("Hello, World!")
.run();
@@ -5108,7 +5107,7 @@ fn duplicate_script_with_extra_env() {
p.cargo("test --workspace -Z doctest-xcompile --doc --target")
.arg(&target)
.masquerade_as_nightly_cargo(&["doctest-xcompile"])
- .with_stdout_contains("test src/lib.rs - (line 2) ... ok")
+ .with_stdout_contains("test foo/src/lib.rs - (line 2) ... ok")
.run();
}
}
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs
index 33889dffa..67cd948ae 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs
index a9cc20575..ce5e9b71d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs
index 63605d8dc..ced39b99c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs
@@ -2,12 +2,28 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4")
+ .feature("clippy", &[])
+ .feature("heapsize", &[])
+ .feature("heapsize_impl", &[])
+ .feature("nightly", &[])
+ .feature("serde", &[])
+ .feature("serde_impl", &[])
+ .feature("serde_test", &[])
+ .publish();
+ cargo_test_support::registry::Package::new("inflector", "0.11.4")
+ .feature("default", &["heavyweight", "lazy_static", "regex"])
+ .feature("heavyweight", &[])
+ .feature("lazy_static", &[])
+ .feature("regex", &[])
+ .feature("unstable", &[])
+ .publish();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/in b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/in
index 6c6a27fcf..6c6a27fcf 120000
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/in
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/in
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/mod.rs
index 705182f20..905b8c8a2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/mod.rs
@@ -2,25 +2,20 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
- let git_url = url::Url::from_directory_path(cwd.join("does-not-exist"))
- .unwrap()
- .to_string();
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .args(["fake-git", "--git", &git_url])
+ .arg_line("+nightly")
.current_dir(cwd)
.assert()
- .code(101)
+ .failure()
.stdout_matches_path(curr_dir!().join("stdout.log"))
.stderr_matches_path(curr_dir!().join("stderr.log"));
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/out/Cargo.toml
index 3ecdb6681..3ecdb6681 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log
new file mode 100644
index 000000000..0593685ad
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log
@@ -0,0 +1,2 @@
+error: invalid character `+` in dependency name: `+nightly`
+ Use `cargo +nightly add` if you meant to use the `nightly` toolchain.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs
index 130ecfbb0..001dd3a8f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-build-package1", "my-build-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs
index b0bb2e03b..285ec658a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs
@@ -2,11 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
-
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .alternative(true)
+ .publish();
+ }
+
let project =
Project::from_template("tests/testsuite/cargo_add/build_prefer_existing_version/in");
let project_root = project.root();
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs
index 94309b3ab..d1540c8ad 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs
index 5dffac323..8b4c6123a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs
@@ -2,12 +2,17 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("test_cyclic_features", "0.1.1")
+ .feature("default", &["feature-one", "feature-two"])
+ .feature("feature-one", &["feature-two"])
+ .feature("feature-two", &["feature-one"])
+ .publish();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs
index 88bdd8065..9d313b9d9 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs
index 10d4e4e98..c4fdd4f3e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs
index 10d4e4e98..c4fdd4f3e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs
index 065fb4f93..2b9550ce0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs
index 11ab2b1bf..8067166dc 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs
index 7557b520d..423f1a26d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs
index 112e92285..c838f1eac 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-dev-package1", "my-dev-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs
index 3f57c6b76..70432b529 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs
index 1785ac820..2d3698c14 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .alternative(true)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs
index 209d20873..438ec7b73 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs
index f6c507188..d612a07f7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs
index 5e4115390..aebae9b3b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs
index 81dffc1ee..d58c6a292 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs
index db47f860d..4189be7ea 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs
index ed99a3111..708fb4ddf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs
index 2ef212e59..4ee483110 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs
index 7fd8d9529..552319402 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs
index 9f59a0353..dff50a61f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs
index bd82b3015..507fb417d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs
index 051564566..8235a7c3c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs
index f123298ae..86b268b59 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs
index 9e14a4007..96be4f46e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs
index 52183adf4..4a3ded8c2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs
index a708a8ae7..a915f3472 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs
index 39eb6e626..3ffe10714 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs
index 03d861856..dbad20105 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs
index 6bf6f8933..e44d71470 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver)
+ .alternative(true)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs
index 612607203..b8a669c7a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs
index b355b1706..706fc9ab7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs
index 94533f979..f2b49376a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs
@@ -2,12 +2,13 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1").publish();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs
index 265a571bc..a4e4d268a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log
deleted file mode 100644
index 18656300b..000000000
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log
+++ /dev/null
@@ -1,12 +0,0 @@
- Updating git repository `[ROOTURL]/case/does-not-exist/`
-...
-error: failed to load source for dependency `fake-git`
-
-Caused by:
- Unable to update [ROOTURL]/case/does-not-exist/
-
-Caused by:
- failed to clone into: [ROOT]/home/.cargo/git/db/does-not-exist-[..]
-
-Caused by:
-...
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs
index 0aff8c090..2f407105d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs
index e385cfc3f..4a2d01765 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs
index 16e041738..8cb9b5129 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs
index 0d26b552d..d8e6a34a6 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs
index 10d841475..aec088020 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs
index a64190f44..4bb32531b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs
index da93c4eb8..43b3a4a79 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs
index c3b4d1f97..2ad903d1f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs
index e1e1b212f..0dbf824d0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs
index 22733b883..e58de1c1d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs
@@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+ 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.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs
index f520b2aca..0aa20873e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs
@@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+ 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.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs
index 9e3e57fe5..5de047f57 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs
index aba9918f7..613d8c863 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs
index 33889dffa..4e99a18ef 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package", "unrelateed-crate"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs
index 008c2d33d..768cdb3a3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
index cd7e94e09..be7a1546b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
@@ -1,6 +1,7 @@
mod add_basic;
mod add_multiple;
mod add_normalized_name_external;
+mod add_toolchain;
mod build;
mod build_prefer_existing_version;
mod change_rename_target;
@@ -36,7 +37,6 @@ mod git_rev;
mod git_tag;
mod infer_prerelease;
mod invalid_arg;
-mod invalid_git_external;
mod invalid_git_name;
mod invalid_key_inherit_dependency;
mod invalid_key_overwrite_inherit_dependency;
@@ -96,6 +96,7 @@ mod path_dev;
mod path_inferred_name;
mod path_inferred_name_conflicts_full_feature;
mod path_normalized_name;
+mod preserve_features_table;
mod preserve_sorted;
mod preserve_unsorted;
mod quiet;
@@ -114,95 +115,3 @@ mod vers;
mod workspace_name;
mod workspace_path;
mod workspace_path_dev;
-
-fn init_registry() {
- cargo_test_support::registry::init();
- add_registry_packages(false);
-}
-
-fn init_alt_registry() {
- cargo_test_support::registry::alt_init();
- add_registry_packages(true);
-}
-
-fn add_registry_packages(alt: bool) {
- for name in [
- "my-package",
- "my-package1",
- "my-package2",
- "my-dev-package1",
- "my-dev-package2",
- "my-build-package1",
- "my-build-package2",
- "toml",
- "versioned-package",
- "cargo-list-test-fixture-dependency",
- "unrelateed-crate",
- ] {
- cargo_test_support::registry::Package::new(name, "0.1.1+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.2.0+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.2.3+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.4.1+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "20.0.0+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "99999.0.0+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "99999.0.0-alpha.1+my-package")
- .alternative(alt)
- .publish();
- }
-
- cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new("test_breaking", "0.2.0")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new("test_nonbreaking", "0.1.1")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new("test_cyclic_features", "0.1.1")
- .alternative(alt)
- .feature("default", &["feature-one", "feature-two"])
- .feature("feature-one", &["feature-two"])
- .feature("feature-two", &["feature-one"])
- .publish();
-
- // Normalization
- cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4")
- .alternative(alt)
- .feature("clippy", &[])
- .feature("heapsize", &[])
- .feature("heapsize_impl", &[])
- .feature("nightly", &[])
- .feature("serde", &[])
- .feature("serde_impl", &[])
- .feature("serde_test", &[])
- .publish();
- cargo_test_support::registry::Package::new("inflector", "0.11.4")
- .alternative(alt)
- .feature("default", &["heavyweight", "lazy_static", "regex"])
- .feature("heavyweight", &[])
- .feature("lazy_static", &[])
- .feature("regex", &[])
- .feature("unstable", &[])
- .publish();
-
- cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
- .alternative(alt)
- .feature("nose", &[])
- .feature("mouth", &[])
- .feature("eyes", &[])
- .feature("ears", &[])
- .publish();
-}
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs
index 10f824484..3cdad0e72 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs
@@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package1", ver).publish();
+ }
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs
index 293ed3eea..9a94dcffa 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs
index 90fda1a9f..063072c79 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package", "my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs
index 7eca17b56..aca1e5422 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs
index e72ca3be2..8a5d41c8d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
index fdb983b21..9145528bf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs
index ae7485979..055b6a8a2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
index 94d1cbf34..408a46ed3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs
index 88bdd8065..9d313b9d9 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs
index e72ca3be2..8a5d41c8d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs
index 0b2ab18b8..da55fced2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs
index ab89e3a6d..fd63cc709 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs
index 161783282..2b9550ce0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs
@@ -6,6 +6,8 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs
index 065fb4f93..2b9550ce0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs
index 065fb4f93..2b9550ce0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs
index 356b4d788..52ad6968a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs
@@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("unrelateed-crate", ver).publish();
+ }
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs
index b418c7809..ea37368d5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs
@@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
+ .alternative(true)
+ .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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs
index 193c5880b..120be4429 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs
@@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
+ .alternative(true)
+ .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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs
index e72ca3be2..8a5d41c8d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs
index 88bdd8065..9d313b9d9 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
index fdb983b21..9145528bf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
index 94d1cbf34..408a46ed3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
index 94d1cbf34..408a46ed3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
index c34c293f9..3090a7527 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
@@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package2", ver).publish();
+ }
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs
index f04405a34..fc057d852 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs
@@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
+ .alternative(true)
+ .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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs
index 32674e23d..bb93cfa15 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs
index 0b2ab18b8..da55fced2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs
index a006c95fd..ea49a07c1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs
index e14282bc1..af96caef5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs
index c0ca2e552..734d0b99a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs
index ce7a0acb0..fcf0aa5e6 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs
index ab89e3a6d..fd63cc709 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs
index 05cc2d109..49a0ae0eb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("versioned-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs
index 87ed58f7f..15cfa571c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs
index 87ed58f7f..15cfa571c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs
index ab89e3a6d..fd63cc709 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs
index 4ae04c70a..478e602a2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs
index ab89e3a6d..fd63cc709 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs
index eadd096aa..17c328ea6 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs
@@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs
index 754f2783f..8ae8a4e7d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml
new file mode 100644
index 000000000..d32f5eb82
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml
@@ -0,0 +1,21 @@
+[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 = { version = "99999.0.0", optional = true }
+
+[features]
+default = [
+ "a",
+ "b",
+ "c",
+]
+a = [
+ "your-face?/nose", # but not the mouth and nose
+]
+b = []
+c = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs
new file mode 100644
index 000000000..1998fa742
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_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_features_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml
new file mode 100644
index 000000000..39acd1016
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml
@@ -0,0 +1,21 @@
+[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 = { version = "99999.0.0" }
+
+[features]
+default = [
+ "a",
+ "b",
+ "c",
+]
+a = [
+ "your-face/nose", # but not the mouth and nose
+]
+b = []
+c = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log
new file mode 100644
index 000000000..796b9601b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_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_features_table/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs
index 4dfb06ed1..aef0d3d2f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package", "versioned-package", "toml"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs
index 4dfb06ed1..aef0d3d2f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package", "versioned-package", "toml"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs
index 357843901..1a02b0802 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs
index d5ba9ef28..d0d937815 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs
@@ -2,12 +2,27 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_alt_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_alt_registry();
+ cargo_test_support::registry::alt_init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver)
+ .alternative(true)
+ .publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs
index 3fefcccf3..28c92d770 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs
index d99e4482a..1998fa742 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs
@@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
index 9aa11eaac..a382d95f1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
@@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
-
+ cargo_test_support::registry::init();
cargo_test_support::registry::Package::new("rust-version-user", "0.1.0")
.rust_version("1.66")
.publish();
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs
index 6baad0e05..31fb57786 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs
@@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
-
+ cargo_test_support::registry::init();
cargo_test_support::registry::Package::new("rust-version-user", "0.1.0")
.rust_version("1.66")
.publish();
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs
index 60e38960f..7a42c566e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs
@@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
-
+ cargo_test_support::registry::init();
cargo_test_support::registry::Package::new("rust-version-user", "0.1.0")
.rust_version("1.66")
.publish();
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs
index 60e38960f..7a42c566e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs
@@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
-
+ cargo_test_support::registry::init();
cargo_test_support::registry::Package::new("rust-version-user", "0.1.0")
.rust_version("1.66")
.publish();
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs
index 55e4c2281..46e8708a1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs
@@ -2,12 +2,30 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in [
+ "unrelateed-crate",
+ "versioned-package",
+ "toml",
+ "my-build-package1",
+ ] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs
index e263bad36..b47874949 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs
index 43efe8e8d..0478ece76 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs
@@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for name in ["my-package1", "my-package2"] {
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new(name, ver).publish();
+ }
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs
index fb78739e9..ee7dab2d1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs
@@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("my-package", ver).publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs
index ccaf850f9..2ab0807de 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs
index ab89e3a6d..fd63cc709 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs
index 4ae04c70a..478e602a2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs
@@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::Project;
-use crate::cargo_add::init_registry;
use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ for ver in [
+ "0.1.1+my-package",
+ "0.2.0+my-package",
+ "0.2.3+my-package",
+ "0.4.1+my-package",
+ "20.0.0+my-package",
+ "99999.0.0+my-package",
+ "99999.0.0-alpha.1+my-package",
+ ] {
+ cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver)
+ .publish();
+ }
+
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
diff --git a/src/tools/cargo/tests/testsuite/cargo_features.rs b/src/tools/cargo/tests/testsuite/cargo_features.rs
index 6e5531431..ed5f53a1e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_features.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_features.rs
@@ -295,6 +295,7 @@ fn allow_features_to_rustc() {
.file(
"src/lib.rs",
r#"
+ #![allow(internal_features)]
#![feature(test_2018_feature)]
"#,
)
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml
new file mode 100644
index 000000000..55ee0423c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
+
+[workspace.lints.rust]
+unsafe_code = "forbid"
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs
new file mode 100644
index 000000000..7d12d9af8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/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/inherit_workspace_lints/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs
new file mode 100644
index 000000000..0b7697d20
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs
@@ -0,0 +1,24 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::ChannelChanger;
+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", "-Zlints"])
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["lints"])
+ .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/inherit_workspace_lints/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml
new file mode 100644
index 000000000..55ee0423c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
+
+[workspace.lints.rust]
+unsafe_code = "forbid"
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..0f3fe5d94
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml
@@ -0,0 +1,11 @@
+[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]
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/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/inherit_workspace_lints/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/src/lib.rs
new file mode 100644
index 000000000..7d12d9af8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/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/inherit_workspace_lints/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml
index b7a2e9036..cef4dd48b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml
@@ -1,4 +1,5 @@
[workspace]
+resolver = "2"
members = [
"crates/*",
]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml
index b7a2e9036..cef4dd48b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml
@@ -1,4 +1,5 @@
[workspace]
+resolver = "2"
members = [
"crates/*",
]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml
index b7a2e9036..cef4dd48b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml
@@ -1,4 +1,5 @@
[workspace]
+resolver = "2"
members = [
"crates/*",
]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml
index b7a2e9036..cef4dd48b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml
@@ -1,4 +1,5 @@
[workspace]
+resolver = "2"
members = [
"crates/*",
]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
index 762a70b34..e895cf883 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
@@ -1,3 +1,4 @@
+mod inherit_workspace_lints;
mod inherit_workspace_package_table;
mod inherit_workspace_package_table_with_edition;
mod inherit_workspace_package_table_with_registry;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs
index 59a2333d6..17bc3f949 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs
index f4c9dcb94..72ce478b9 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs
index 7d61fa954..d9cbdf1a7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs
index dca189315..2e097135d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
index 2c1d592fb..ec521a5bb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
@@ -5,11 +5,9 @@ use cargo_test_support::git;
use cargo_test_support::project;
use cargo_test_support::CargoCommand;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
let git_project1 = git::new("bar1", |project| {
project
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs
index 7047c92e2..98b99bec3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs
@@ -3,11 +3,22 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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("toml", "0.2.3+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs
index 717adef3e..cbcf5bc07 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs
@@ -3,11 +3,22 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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("toml", "0.2.3+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs
index eac3c8b46..97d5c8625 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs
index c4dbeae91..fb32a075f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs
index bff09882e..d7f84c035 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs
index 5093d5d2d..d14179e0c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs
index 80d42be1d..94d475059 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs
index 7be8fd628..c20cd94d2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs
index 34deb6cb8..aba040a2b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs
index e04418fa8..f187e609c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
index fd8b4a233..feb08cea4 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
@@ -28,61 +28,3 @@ mod update_lock_file;
mod workspace;
mod workspace_non_virtual;
mod workspace_preserved;
-
-fn init_registry() {
- cargo_test_support::registry::init();
- add_registry_packages(false);
-}
-
-fn add_registry_packages(alt: bool) {
- for name in [
- "clippy",
- "dbus",
- "docopt",
- "ncurses",
- "pad",
- "regex",
- "rustc-serialize",
- "toml",
- ] {
- cargo_test_support::registry::Package::new(name, "0.1.1+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.2.0+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.2.3+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.4.1+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.6.2+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "0.9.9+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "1.0.90+my-package")
- .alternative(alt)
- .publish();
- cargo_test_support::registry::Package::new(name, "20.0.0+my-package")
- .alternative(alt)
- .publish();
- }
-
- for name in ["semver", "serde"] {
- cargo_test_support::registry::Package::new(name, "0.1.1")
- .alternative(alt)
- .feature("std", &[])
- .publish();
- cargo_test_support::registry::Package::new(name, "0.9.0")
- .alternative(alt)
- .feature("std", &[])
- .publish();
- cargo_test_support::registry::Package::new(name, "1.0.90")
- .alternative(alt)
- .feature("std", &[])
- .publish();
- }
-}
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs
index 35922b738..d0af30a30 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs
index 5eac7e2f8..7ec2bd0b7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs
index d0c66f9b0..f9e0e5548 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs
index d03463927..e227eb095 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs
index cae736b34..ce8fcf712 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs
index af54226bb..9ab3c4c24 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs
index 2714f3197..73d01b89d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs
index 53381e6bc..7c92026a5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs
index 1447c753d..6f6fc5c61 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs
index 11afbbf8f..7adffa229 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs
index d303c2b85..005eb7871 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs
@@ -3,11 +3,23 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("ncurses", "20.0.0+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs
index be5bc87f5..00e7964e4 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.1+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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs
index 225fbec00..5274b07d7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs
index 225fbec00..5274b07d7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs
index 225fbec00..5274b07d7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs
@@ -3,11 +3,21 @@ use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::Project;
-use crate::cargo_remove::init_registry;
-
#[cargo_test]
fn case() {
- init_registry();
+ 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;
diff --git a/src/tools/cargo/tests/testsuite/config.rs b/src/tools/cargo/tests/testsuite/config.rs
index b0f9d167b..5d4163818 100644
--- a/src/tools/cargo/tests/testsuite/config.rs
+++ b/src/tools/cargo/tests/testsuite/config.rs
@@ -1,7 +1,7 @@
//! Tests for config settings.
use cargo::core::{PackageIdSpec, Shell};
-use cargo::util::config::{self, Config, Definition, SslVersionConfig, StringList};
+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::CargoResult;
@@ -1651,3 +1651,63 @@ fn debuginfo_parsing() {
.ends_with("could not load config key `profile.dev.debug`"));
}
}
+
+#[cargo_test]
+fn build_jobs_missing() {
+ write_config(
+ "\
+[build]
+",
+ );
+
+ let config = new_config();
+
+ assert!(config
+ .get::<Option<JobsConfig>>("build.jobs")
+ .unwrap()
+ .is_none());
+}
+
+#[cargo_test]
+fn build_jobs_default() {
+ write_config(
+ "\
+[build]
+jobs = \"default\"
+",
+ );
+
+ let config = new_config();
+
+ let a = config
+ .get::<Option<JobsConfig>>("build.jobs")
+ .unwrap()
+ .unwrap();
+
+ match a {
+ JobsConfig::String(v) => assert_eq!(&v, "default"),
+ JobsConfig::Integer(_) => panic!("Did not except an integer."),
+ }
+}
+
+#[cargo_test]
+fn build_jobs_integer() {
+ write_config(
+ "\
+[build]
+jobs = 2
+",
+ );
+
+ let config = new_config();
+
+ let a = config
+ .get::<Option<JobsConfig>>("build.jobs")
+ .unwrap()
+ .unwrap();
+
+ match a {
+ JobsConfig::String(_) => panic!("Did not except an integer."),
+ JobsConfig::Integer(v) => assert_eq!(v, 2),
+ }
+}
diff --git a/src/tools/cargo/tests/testsuite/config_include.rs b/src/tools/cargo/tests/testsuite/config_include.rs
index ae568065a..057c583ae 100644
--- a/src/tools/cargo/tests/testsuite/config_include.rs
+++ b/src/tools/cargo/tests/testsuite/config_include.rs
@@ -6,9 +6,9 @@ use cargo_test_support::{no_such_file_err_msg, project};
#[cargo_test]
fn gated() {
// Requires -Z flag.
- write_config("include='other'");
+ write_config("include='other.toml'");
write_config_at(
- ".cargo/other",
+ ".cargo/other.toml",
"
othervalue = 1
",
@@ -25,13 +25,13 @@ fn simple() {
write_config_at(
".cargo/config",
"
- include = 'other'
+ include = 'other.toml'
key1 = 1
key2 = 2
",
);
write_config_at(
- ".cargo/other",
+ ".cargo/other.toml",
"
key2 = 3
key3 = 4
@@ -84,39 +84,63 @@ fn works_with_cli() {
}
#[cargo_test]
-fn left_to_right() {
- // How it merges multiple includes.
+fn left_to_right_bottom_to_top() {
+ // How it merges multiple nested includes.
write_config_at(
".cargo/config",
"
- include = ['one', 'two']
- primary = 1
+ include = ['left-middle.toml', 'right-middle.toml']
+ top = 1
+ ",
+ );
+ write_config_at(
+ ".cargo/right-middle.toml",
+ "
+ include = 'right-bottom.toml'
+ top = 0
+ right-middle = 0
",
);
write_config_at(
- ".cargo/one",
+ ".cargo/right-bottom.toml",
"
- one = 1
- primary = 2
+ top = -1
+ right-middle = -1
+ right-bottom = -1
",
);
write_config_at(
- ".cargo/two",
+ ".cargo/left-middle.toml",
"
- two = 2
- primary = 3
+ include = 'left-bottom.toml'
+ top = -2
+ right-middle = -2
+ right-bottom = -2
+ left-middle = -2
+ ",
+ );
+ write_config_at(
+ ".cargo/left-bottom.toml",
+ "
+ top = -3
+ right-middle = -3
+ right-bottom = -3
+ left-middle = -3
+ left-bottom = -3
",
);
let config = ConfigBuilder::new().unstable_flag("config-include").build();
- assert_eq!(config.get::<i32>("primary").unwrap(), 1);
- assert_eq!(config.get::<i32>("one").unwrap(), 1);
- assert_eq!(config.get::<i32>("two").unwrap(), 2);
+ assert_eq!(config.get::<i32>("top").unwrap(), 1);
+ assert_eq!(config.get::<i32>("right-middle").unwrap(), 0);
+ assert_eq!(config.get::<i32>("right-bottom").unwrap(), -1);
+ assert_eq!(config.get::<i32>("left-middle").unwrap(), -2);
+ assert_eq!(config.get::<i32>("left-bottom").unwrap(), -3);
}
#[cargo_test]
fn missing_file() {
// Error when there's a missing file.
- write_config("include='missing'");
+ write_config("include='missing.toml'");
let config = ConfigBuilder::new()
.unstable_flag("config-include")
.build_err();
@@ -127,10 +151,10 @@ fn missing_file() {
could not load Cargo configuration
Caused by:
- failed to load config include `missing` from `[..]/.cargo/config`
+ failed to load config include `missing.toml` from `[..]/.cargo/config`
Caused by:
- failed to read configuration file `[..]/.cargo/missing`
+ failed to read configuration file `[..]/.cargo/missing.toml`
Caused by:
{}",
@@ -140,11 +164,29 @@ Caused by:
}
#[cargo_test]
+fn wrong_file_extension() {
+ // Error when it doesn't end with `.toml`.
+ write_config("include='config.png'");
+ let config = ConfigBuilder::new()
+ .unstable_flag("config-include")
+ .build_err();
+ assert_error(
+ config.unwrap_err(),
+ "\
+could not load Cargo configuration
+
+Caused by:
+ expected a config include path ending with `.toml`, but found `config.png` from `[..]/.cargo/config`
+",
+ );
+}
+
+#[cargo_test]
fn cycle() {
// Detects a cycle.
- write_config_at(".cargo/config", "include='one'");
- write_config_at(".cargo/one", "include='two'");
- write_config_at(".cargo/two", "include='config'");
+ write_config_at(".cargo/config.toml", "include='one.toml'");
+ write_config_at(".cargo/one.toml", "include='two.toml'");
+ write_config_at(".cargo/two.toml", "include='config.toml'");
let config = ConfigBuilder::new()
.unstable_flag("config-include")
.build_err();
@@ -154,16 +196,16 @@ fn cycle() {
could not load Cargo configuration
Caused by:
- failed to load config include `one` from `[..]/.cargo/config`
+ failed to load config include `one.toml` from `[..]/.cargo/config.toml`
Caused by:
- failed to load config include `two` from `[..]/.cargo/one`
+ failed to load config include `two.toml` from `[..]/.cargo/one.toml`
Caused by:
- failed to load config include `config` from `[..]/.cargo/two`
+ failed to load config include `config.toml` from `[..]/.cargo/two.toml`
Caused by:
- config `include` cycle detected with path `[..]/.cargo/config`",
+ config `include` cycle detected with path `[..]/.cargo/config.toml`",
);
}
@@ -178,10 +220,10 @@ fn cli_include() {
bar = 2
",
);
- write_config_at(".cargo/config-foo", "foo = 2");
+ write_config_at(".cargo/config-foo.toml", "foo = 2");
let config = ConfigBuilder::new()
.unstable_flag("config-include")
- .config_arg("include='.cargo/config-foo'")
+ .config_arg("include='.cargo/config-foo.toml'")
.build();
assert_eq!(config.get::<i32>("foo").unwrap(), 2);
assert_eq!(config.get::<i32>("bar").unwrap(), 2);
@@ -209,7 +251,7 @@ fn cli_include_failed() {
// Error message when CLI include fails to load.
let config = ConfigBuilder::new()
.unstable_flag("config-include")
- .config_arg("include='foobar'")
+ .config_arg("include='foobar.toml'")
.build_err();
assert_error(
config.unwrap_err(),
@@ -218,10 +260,10 @@ fn cli_include_failed() {
failed to load --config include
Caused by:
- failed to load config include `foobar` from `--config cli option`
+ failed to load config include `foobar.toml` from `--config cli option`
Caused by:
- failed to read configuration file `[..]/foobar`
+ failed to read configuration file `[..]/foobar.toml`
Caused by:
{}",
@@ -235,14 +277,14 @@ fn cli_merge_failed() {
// Error message when CLI include merge fails.
write_config("foo = ['a']");
write_config_at(
- ".cargo/other",
+ ".cargo/other.toml",
"
foo = 'b'
",
);
let config = ConfigBuilder::new()
.unstable_flag("config-include")
- .config_arg("include='.cargo/other'")
+ .config_arg("include='.cargo/other.toml'")
.build_err();
// Maybe this error message should mention it was from an include file?
assert_error(
@@ -251,7 +293,7 @@ fn cli_merge_failed() {
failed to merge --config key `foo` into `[..]/.cargo/config`
Caused by:
- failed to merge config value from `[..]/.cargo/other` into `[..]/.cargo/config`: \
+ failed to merge config value from `[..]/.cargo/other.toml` into `[..]/.cargo/config`: \
expected array, but found string",
);
}
diff --git a/src/tools/cargo/tests/testsuite/cross_compile.rs b/src/tools/cargo/tests/testsuite/cross_compile.rs
index cc9644550..1bc0c277d 100644
--- a/src/tools/cargo/tests/testsuite/cross_compile.rs
+++ b/src/tools/cargo/tests/testsuite/cross_compile.rs
@@ -398,7 +398,7 @@ fn linker() {
"\
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `rustc --crate-name foo src/foo.rs [..]--crate-type bin \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [CWD]/target/{target}/debug/deps \
--target {target} \
diff --git a/src/tools/cargo/tests/testsuite/custom_target.rs b/src/tools/cargo/tests/testsuite/custom_target.rs
index b7ad4d835..491d3233c 100644
--- a/src/tools/cargo/tests/testsuite/custom_target.rs
+++ b/src/tools/cargo/tests/testsuite/custom_target.rs
@@ -4,6 +4,7 @@ use cargo_test_support::{basic_manifest, project};
use std::fs;
const MINIMAL_LIB: &str = r#"
+#![allow(internal_features)]
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
@@ -80,6 +81,7 @@ fn custom_target_dependency() {
.file(
"src/lib.rs",
r#"
+ #![allow(internal_features)]
#![feature(no_core)]
#![feature(lang_items)]
#![feature(auto_traits)]
diff --git a/src/tools/cargo/tests/testsuite/directory.rs b/src/tools/cargo/tests/testsuite/directory.rs
index 0e28de039..e72f1f07d 100644
--- a/src/tools/cargo/tests/testsuite/directory.rs
+++ b/src/tools/cargo/tests/testsuite/directory.rs
@@ -188,7 +188,9 @@ fn simple_install_fail() {
.with_status(101)
.with_stderr(
" Installing bar v0.1.0
-error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]`
+error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]`.
+To reuse those artifacts with a future compilation, set the environment variable \
+`CARGO_TARGET_DIR` to that path.
Caused by:
no matching package found
@@ -759,7 +761,9 @@ fn version_missing() {
.with_stderr(
"\
[INSTALLING] bar v0.1.0
-error: failed to compile [..]
+error: failed to compile [..], intermediate artifacts can be found at `[..]`.
+To reuse those artifacts with a future compilation, set the environment variable \
+`CARGO_TARGET_DIR` to that path.
Caused by:
failed to select a version for the requirement `foo = \"^2\"`
diff --git a/src/tools/cargo/tests/testsuite/doc.rs b/src/tools/cargo/tests/testsuite/doc.rs
index 739bcf376..481df8590 100644
--- a/src/tools/cargo/tests/testsuite/doc.rs
+++ b/src/tools/cargo/tests/testsuite/doc.rs
@@ -756,6 +756,7 @@ fn doc_target() {
.file(
"src/lib.rs",
r#"
+ #![allow(internal_features)]
#![feature(no_core, lang_items)]
#![no_core]
@@ -2041,7 +2042,7 @@ fn crate_versions_flag_is_overridden() {
asserts(output_documentation());
}
-#[cargo_test(nightly, reason = "-Zdoctest-in-workspace is unstable")]
+#[cargo_test]
fn doc_test_in_workspace() {
let p = project()
.file(
@@ -2087,8 +2088,7 @@ fn doc_test_in_workspace() {
",
)
.build();
- p.cargo("test -Zdoctest-in-workspace --doc -vv")
- .masquerade_as_nightly_cargo(&["doctest-in-workspace"])
+ p.cargo("test --doc -vv")
.with_stderr_contains("[DOCTEST] crate-a")
.with_stdout_contains(
"
@@ -2096,7 +2096,6 @@ running 1 test
test crate-a/src/lib.rs - (line 1) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
-
",
)
.with_stderr_contains("[DOCTEST] crate-b")
@@ -2106,7 +2105,98 @@ running 1 test
test crate-b/src/lib.rs - (line 1) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
+",
+ )
+ .run();
+}
+
+/// This is a test for <https://github.com/rust-lang/rust/issues/46372>.
+/// The `file!()` macro inside of an `include!()` should output
+/// workspace-relative paths, just like it does in other cases.
+#[cargo_test]
+fn doc_test_include_file() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = [
+ "child",
+ ]
+ [package]
+ name = "root"
+ version = "0.1.0"
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ /// ```
+ /// assert_eq!("src/lib.rs", file!().replace("\\", "/"))
+ /// ```
+ pub mod included {
+ include!(concat!("../", file!(), ".included.rs"));
+ }
+ "#,
+ )
+ .file(
+ "src/lib.rs.included.rs",
+ r#"
+ /// ```
+ /// assert_eq!(1, 1)
+ /// ```
+ pub fn foo() {}
+ "#,
+ )
+ .file(
+ "child/Cargo.toml",
+ r#"
+ [package]
+ name = "child"
+ version = "0.1.0"
+ "#,
+ )
+ .file(
+ "child/src/lib.rs",
+ r#"
+ /// ```
+ /// assert_eq!("child/src/lib.rs", file!().replace("\\", "/"))
+ /// ```
+ pub mod included {
+ include!(concat!("../../", file!(), ".included.rs"));
+ }
+ "#,
+ )
+ .file(
+ "child/src/lib.rs.included.rs",
+ r#"
+ /// ```
+ /// assert_eq!(1, 1)
+ /// ```
+ pub fn foo() {}
+ "#,
+ )
+ .build();
+
+ p.cargo("test --workspace --doc -vv -- --test-threads=1")
+ .with_stderr_contains("[DOCTEST] child")
+ .with_stdout_contains(
+ "
+running 2 tests
+test child/src/../../child/src/lib.rs.included.rs - included::foo (line 2) ... ok
+test child/src/lib.rs - included (line 2) ... ok
+
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
+",
+ )
+ .with_stderr_contains("[DOCTEST] root")
+ .with_stdout_contains(
+ "
+running 2 tests
+test src/../src/lib.rs.included.rs - included::foo (line 2) ... ok
+test src/lib.rs - included (line 2) ... ok
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/features.rs b/src/tools/cargo/tests/testsuite/features.rs
index 848e05677..557fab14a 100644
--- a/src/tools/cargo/tests/testsuite/features.rs
+++ b/src/tools/cargo/tests/testsuite/features.rs
@@ -1937,8 +1937,8 @@ fn nonexistent_required_features() {
}
#[cargo_test]
-fn invalid_feature_names_warning() {
- // Warnings for more restricted feature syntax.
+fn invalid_feature_names_error() {
+ // Errors for more restricted feature syntax.
let p = project()
.file(
"Cargo.toml",
@@ -1948,72 +1948,57 @@ fn invalid_feature_names_warning() {
version = "0.1.0"
[features]
- # Some valid, but unusual names, shouldn't warn.
- "c++17" = []
- "128bit" = []
- "_foo" = []
- "feat-name" = []
- "feat_name" = []
- "foo.bar" = []
-
- # Invalid names.
+ # Invalid start character.
"+foo" = []
- "-foo" = []
- ".foo" = []
- "foo:bar" = []
- "foo?" = []
- "?foo" = []
- "ⒶⒷⒸ" = []
- "a¼" = []
"#,
)
.file("src/lib.rs", "")
.build();
- // Unfortunately the warnings are duplicated due to the Summary being
- // loaded twice (once in the Workspace, and once in PackageRegistry) and
- // Cargo does not have a de-duplication system. This should probably be
- // OK, since I'm not expecting this to affect anyone.
p.cargo("check")
- .with_stderr("\
-[WARNING] invalid character `+` in feature `+foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `-` in feature `-foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `.` in feature `.foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `?` in feature `?foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `¼` in feature `a¼` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `:` in feature `foo:bar` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `?` in feature `foo?` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `Ⓐ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `Ⓑ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[WARNING] invalid character `Ⓒ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
-This was previously accepted but is being phased out; it will become a hard error in a future release.
-For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project.
-[CHECKING] foo v0.1.0 [..]
-[FINISHED] [..]
-")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
+
+Caused by:
+ invalid character `+` in feature `+foo` in package foo v0.1.0 ([ROOT]/foo), \
+ the first character must be a Unicode XID start character or digit \
+ (most letters or `_` or `0` to `9`)
+",
+ )
+ .run();
+
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ # Invalid continue character.
+ "a&b" = []
+ "#,
+ );
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+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 `.` \
+ (numbers, `+`, `-`, `_`, `.`, or most letters)
+",
+ )
.run();
}
#[cargo_test]
-fn invalid_feature_names_error() {
+fn invalid_feature_name_slash_error() {
// Errors for more restricted feature syntax.
let p = project()
.file(
diff --git a/src/tools/cargo/tests/testsuite/features2.rs b/src/tools/cargo/tests/testsuite/features2.rs
index 494c83f1e..68fecb863 100644
--- a/src/tools/cargo/tests/testsuite/features2.rs
+++ b/src/tools/cargo/tests/testsuite/features2.rs
@@ -1407,6 +1407,41 @@ workspace: [..]/foo/Cargo.toml
}
#[cargo_test]
+fn edition_2021_workspace_member() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = ["a"]
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+ edition = "2021"
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+warning: some crates are on edition 2021 which defaults to `resolver = \"2\"`, but virtual workspaces default to `resolver = \"1\"`
+note: to keep the current resolver, specify `workspace.resolver = \"1\"` in the workspace root's manifest
+note: to use the edition 2021 resolver, specify `workspace.resolver = \"2\"` in the workspace root's manifest
+[CHECKING] a v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn resolver_ws_root_and_member() {
// Check when specified in both ws root and member.
let p = project()
diff --git a/src/tools/cargo/tests/testsuite/future_incompat_report.rs b/src/tools/cargo/tests/testsuite/future_incompat_report.rs
index 9f451a64c..4d2c66d17 100644
--- a/src/tools/cargo/tests/testsuite/future_incompat_report.rs
+++ b/src/tools/cargo/tests/testsuite/future_incompat_report.rs
@@ -164,7 +164,7 @@ fn test_multi_crate() {
second-dep = "*"
"#,
)
- .file("src/main.rs", "fn main() {}")
+ .file("src/lib.rs", "")
.build();
for command in &["build", "check", "rustc", "test"] {
diff --git a/src/tools/cargo/tests/testsuite/git.rs b/src/tools/cargo/tests/testsuite/git.rs
index 7c717e967..f60ee978a 100644
--- a/src/tools/cargo/tests/testsuite/git.rs
+++ b/src/tools/cargo/tests/testsuite/git.rs
@@ -3619,3 +3619,71 @@ fn cleans_temp_pack_files() {
p.cargo("generate-lockfile").run();
assert!(!tmp_path.exists());
}
+
+#[cargo_test]
+fn different_user_relative_submodules() {
+ let user1_git_project = git::new("user1/dep1", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("dep1"))
+ .file("src/lib.rs", "")
+ });
+
+ let user2_git_project = git::new("user2/dep1", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("dep1"))
+ .file("src/lib.rs", "")
+ });
+ let user2_git_project2 = git::new("user2/dep2", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("dep1"))
+ .file("src/lib.rs", "")
+ });
+
+ let user2_repo = git2::Repository::open(&user2_git_project.root()).unwrap();
+ let url = "../dep2";
+ git::add_submodule(&user2_repo, url, Path::new("dep2"));
+ git::commit(&user2_repo);
+
+ let user1_repo = git2::Repository::open(&user1_git_project.root()).unwrap();
+ let url = user2_git_project.url();
+ git::add_submodule(&user1_repo, url.as_str(), Path::new("user2/dep1"));
+ git::commit(&user1_repo);
+
+ let project = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.5.0"
+
+ [dependencies.dep1]
+ git = '{}'
+ "#,
+ user1_git_project.url()
+ ),
+ )
+ .file("src/main.rs", &main_file(r#""hello""#, &[]))
+ .build();
+
+ project
+ .cargo("build")
+ .with_stderr(&format!(
+ "\
+[UPDATING] git repository `{}`
+[UPDATING] git submodule `{}`
+[UPDATING] git submodule `{}`
+[COMPILING] dep1 v0.5.0 ({}#[..])
+[COMPILING] foo v0.5.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ path2url(&user1_git_project.root()),
+ path2url(&user2_git_project.root()),
+ path2url(&user2_git_project2.root()),
+ path2url(&user1_git_project.root()),
+ ))
+ .run();
+
+ assert!(project.bin("foo").is_file());
+}
diff --git a/src/tools/cargo/tests/testsuite/install.rs b/src/tools/cargo/tests/testsuite/install.rs
index 9b881dfdc..0c7fc5037 100644
--- a/src/tools/cargo/tests/testsuite/install.rs
+++ b/src/tools/cargo/tests/testsuite/install.rs
@@ -58,6 +58,20 @@ fn simple() {
}
#[cargo_test]
+fn toolchain() {
+ pkg("foo", "0.0.1");
+
+ cargo_process("install +nightly")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] invalid character `+` in package name: `+nightly`
+ Use `cargo +nightly install` if you meant to use the `nightly` toolchain.",
+ )
+ .run();
+}
+
+#[cargo_test]
fn simple_with_message_format() {
pkg("foo", "0.0.1");
@@ -964,7 +978,8 @@ fn compile_failure() {
"\
[ERROR] could not compile `foo` (bin \"foo\") due to previous error
[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be \
- found at `[..]target`
+ found at `[..]target`.\nTo reuse those artifacts with a future compilation, \
+ set the environment variable `CARGO_TARGET_DIR` to that path.
",
)
.run();
@@ -1269,7 +1284,8 @@ fn reports_unsuccessful_subcommand_result() {
.run();
cargo_process("fail")
.with_status(101)
- .with_stderr_contains("thread '[..]' panicked at 'explicit panic', [..]")
+ .with_stderr_contains("thread '[..]' panicked at [..]src/main.rs:1:[..]")
+ .with_stderr_contains("[..]explicit panic[..]")
.run();
}
@@ -2250,7 +2266,9 @@ fn failed_install_retains_temp_directory() {
)
.unwrap();
compare::match_contains(
- "error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at `[..]`",
+ "error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at \
+ `[..]`.\nTo reuse those artifacts with a future compilation, set the environment \
+ variable `CARGO_TARGET_DIR` to that path.",
&stderr,
None,
)
@@ -2258,7 +2276,7 @@ fn failed_install_retains_temp_directory() {
// Find the path in the output.
let start = stderr.find("found at `").unwrap() + 10;
- let end = stderr[start..].find('\n').unwrap() - 1;
+ let end = stderr[start..].find('.').unwrap() - 1;
let path = Path::new(&stderr[start..(end + start)]);
assert!(path.exists());
assert!(path.join("release/deps").exists());
diff --git a/src/tools/cargo/tests/testsuite/lockfile_compat.rs b/src/tools/cargo/tests/testsuite/lockfile_compat.rs
index aad8723c3..63148cc07 100644
--- a/src/tools/cargo/tests/testsuite/lockfile_compat.rs
+++ b/src/tools/cargo/tests/testsuite/lockfile_compat.rs
@@ -888,3 +888,81 @@ perhaps a crate was updated and forgotten to be re-vendored?
)
.run();
}
+
+#[cargo_test]
+fn v4_is_unstable() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("Cargo.lock", "version = 4")
+ .build();
+
+ p.cargo("fetch")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse lock file at: [CWD]/Cargo.lock
+
+Caused by:
+ lock file version `4` was found, but this version of Cargo does not \
+ understand this lock file, perhaps Cargo needs to be updated?
+",
+ )
+ .run();
+
+ // On nightly, let the user know about the `-Z` flag.
+ p.cargo("fetch")
+ .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"])
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse lock file at: [CWD]/Cargo.lock
+
+Caused by:
+ lock file version 4 requires `-Znext-lockfile-bump`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn v4_cannot_be_created_from_scratch() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("fetch -Znext-lockfile-bump")
+ .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"])
+ .run();
+
+ let lockfile = r#"# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "foo"
+version = "0.0.1"
+"#;
+
+ let lock = p.read_lockfile();
+ assert_match_exact(lockfile, &lock);
+}
diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs
index 170a22667..2c282c0a3 100644
--- a/src/tools/cargo/tests/testsuite/main.rs
+++ b/src/tools/cargo/tests/testsuite/main.rs
@@ -123,6 +123,7 @@ mod rustdoc_extern_html;
mod rustdocflags;
mod rustflags;
mod rustup;
+mod script;
mod search;
mod shell_quoting;
mod source_replacement;
diff --git a/src/tools/cargo/tests/testsuite/profile_config.rs b/src/tools/cargo/tests/testsuite/profile_config.rs
index cf9807964..26104e7e7 100644
--- a/src/tools/cargo/tests/testsuite/profile_config.rs
+++ b/src/tools/cargo/tests/testsuite/profile_config.rs
@@ -267,7 +267,7 @@ fn profile_config_all_options() {
-C panic=abort \
-C lto[..]\
-C codegen-units=2 \
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C debug-assertions=on \
-C overflow-checks=off [..]\
-C rpath [..]\
@@ -437,7 +437,7 @@ fn named_config_profile() {
assert_eq!(p.name, "foo");
assert_eq!(p.codegen_units, Some(2)); // "foo" from config
assert_eq!(p.opt_level, "1"); // "middle" from manifest
- assert_eq!(p.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // "bar" from config
+ assert_eq!(p.debuginfo.into_inner(), TomlDebugInfo::Limited); // "bar" from config
assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override)
assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override)
@@ -446,7 +446,7 @@ fn named_config_profile() {
assert_eq!(bo.name, "foo");
assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config
assert_eq!(bo.opt_level, "0"); // default to zero
- assert_eq!(bo.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal
+ assert_eq!(bo.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal
assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest
assert_eq!(bo.overflow_checks, true); // SAME as normal
@@ -455,7 +455,7 @@ fn named_config_profile() {
assert_eq!(po.name, "foo");
assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config
assert_eq!(po.opt_level, "1"); // SAME as normal
- assert_eq!(po.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal
+ assert_eq!(po.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal
assert_eq!(po.debug_assertions, true); // SAME as normal
assert_eq!(po.overflow_checks, false); // "middle" package override from manifest
}
@@ -509,12 +509,13 @@ fn test_with_dev_profile() {
[DOWNLOADING] [..]
[DOWNLOADED] [..]
[COMPILING] somedep v1.0.0
-[RUNNING] `rustc --crate-name somedep [..]-C debuginfo=0[..]
+[RUNNING] `rustc --crate-name somedep [..]
[COMPILING] foo v0.1.0 [..]
-[RUNNING] `rustc --crate-name foo [..]-C debuginfo=0[..]
+[RUNNING] `rustc --crate-name foo [..]
[FINISHED] [..]
[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]`
",
)
+ .with_stdout_does_not_contain("[..] -C debuginfo=0[..]")
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/profile_targets.rs b/src/tools/cargo/tests/testsuite/profile_targets.rs
index 0449e8ab3..a88ca34fd 100644
--- a/src/tools/cargo/tests/testsuite/profile_targets.rs
+++ b/src/tools/cargo/tests/testsuite/profile_targets.rs
@@ -88,11 +88,11 @@ fn profile_selection_build() {
.with_stderr_unordered("\
[COMPILING] bar [..]
[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]
[COMPILING] bdep [..]
-[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]
[COMPILING] foo [..]
-[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]
[RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build`
[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
@@ -100,6 +100,7 @@ fn profile_selection_build() {
[FINISHED] dev [unoptimized + debuginfo] [..]
"
)
+ .with_stderr_does_not_contain("[..] -C debuginfo=0[..]")
.run();
p.cargo("build -vv")
.with_stderr_unordered(
@@ -154,8 +155,9 @@ fn profile_selection_build_all_targets() {
// `build.rs` is a plugin.
// - Benchmark dependencies are compiled in `dev` mode, which may be
// surprising. See issue rust-lang/cargo#4929.
- // - We make sure that the build dependencies bar, bdep, and build.rs
- // are built with debuginfo=0.
+ // - We make sure that the build dependencies bar, bdep, and build.rs are built with
+ // debuginfo=0; but since we don't pass `-C debuginfo` when it's set to 0, we have to test
+ // explicitly that there's no `-C debuginfo` flag.
//
// - Dependency profiles:
// Pkg Target Profile Reason
@@ -181,24 +183,25 @@ fn profile_selection_build_all_targets() {
[COMPILING] bar [..]
[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]
[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]
[COMPILING] bdep [..]
-[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]
[COMPILING] foo [..]
-[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..]
+[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]
[RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build`
[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]`
-[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]`
+[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]`
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]`
-[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]`
-[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]`
-[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]`
+[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]`
+[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]`
+[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]`
[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]`
[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]`
[FINISHED] dev [unoptimized + debuginfo] [..]
"
)
+ .with_stderr_does_not_contain("[..] -C debuginfo=0[..]")
.run();
p.cargo("build -vv")
.with_stderr_unordered(
@@ -315,10 +318,10 @@ fn profile_selection_test() {
[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..]
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..]
+[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..]
+[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..]
[FINISHED] test [unoptimized + debuginfo] [..]
[RUNNING] `[..]/deps/foo-[..]`
@@ -513,10 +516,10 @@ fn profile_selection_check_all_targets() {
[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..]
+[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]
[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]
[FINISHED] dev [unoptimized + debuginfo] [..]
@@ -615,11 +618,11 @@ fn profile_selection_check_all_targets_test() {
[RUNNING] `[..]target/debug/build/foo-[..]/build-script-build`
[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0
[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]
-[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..]
-[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..]
+[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
+[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]
[FINISHED] test [unoptimized + debuginfo] [..]
").run();
diff --git a/src/tools/cargo/tests/testsuite/profiles.rs b/src/tools/cargo/tests/testsuite/profiles.rs
index 2d2646fe3..465ab3b99 100644
--- a/src/tools/cargo/tests/testsuite/profiles.rs
+++ b/src/tools/cargo/tests/testsuite/profiles.rs
@@ -66,7 +66,7 @@ fn opt_level_override_0() {
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
--emit=[..]link[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
@@ -99,7 +99,7 @@ fn debug_override_1() {
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
--emit=[..]link[..]\
- -C debuginfo=1 \
+ -C debuginfo=1 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
@@ -136,7 +136,7 @@ fn check_opt_level_override(profile_level: &str, rustc_level: &str) {
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
--emit=[..]link \
-C opt-level={level}[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C debug-assertions=on \
-C metadata=[..] \
--out-dir [..] \
@@ -211,7 +211,7 @@ fn top_level_overrides_deps() {
--emit=[..]link \
-C prefer-dynamic \
-C opt-level=1[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [CWD]/target/release/deps \
-L dependency=[CWD]/target/release/deps`
@@ -219,7 +219,7 @@ fn top_level_overrides_deps() {
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
--emit=[..]link \
-C opt-level=1[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/release/deps \
@@ -467,10 +467,11 @@ fn debug_0_report() {
.with_stderr(
"\
[COMPILING] foo v0.1.0 [..]
-[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..]
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]
[FINISHED] dev [unoptimized] target(s) in [..]
",
)
+ .with_stderr_does_not_contain("-C debuginfo")
.run();
}
@@ -742,3 +743,42 @@ Caused by:
)
.run();
}
+
+#[cargo_test(nightly, reason = "debug options stabilized in 1.70")]
+fn debug_options_valid() {
+ let build = |option| {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+
+ [profile.dev]
+ debug = "{option}"
+ "#
+ ),
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("build -v")
+ };
+
+ for (option, cli) in [
+ ("line-directives-only", "line-directives-only"),
+ ("line-tables-only", "line-tables-only"),
+ ("limited", "1"),
+ ("full", "2"),
+ ] {
+ build(option)
+ .with_stderr_contains(&format!("[RUNNING] `rustc [..]-C debuginfo={cli} [..]"))
+ .run();
+ }
+ build("none")
+ .with_stderr_does_not_contain("[..]-C debuginfo[..]")
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/required_features.rs b/src/tools/cargo/tests/testsuite/required_features.rs
index ac6c9d233..88be89fd4 100644
--- a/src/tools/cargo/tests/testsuite/required_features.rs
+++ b/src/tools/cargo/tests/testsuite/required_features.rs
@@ -658,7 +658,9 @@ Consider enabling some of the needed features by passing, e.g., `--features=\"a\
"\
[INSTALLING] foo v0.0.1 ([..])
[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \
- `[..]target`
+ `[..]target`.
+To reuse those artifacts with a future compilation, set the environment \
+variable `CARGO_TARGET_DIR` to that path.
Caused by:
target `foo` in package `foo` requires the features: `a`
@@ -678,7 +680,8 @@ Caused by:
"\
[INSTALLING] foo v0.0.1 ([..])
[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \
- `[..]target`
+ `[..]target`.\nTo reuse those artifacts with a future compilation, set the environment \
+ variable `CARGO_TARGET_DIR` to that path.
Caused by:
target `foo` in package `foo` requires the features: `a`
diff --git a/src/tools/cargo/tests/testsuite/run.rs b/src/tools/cargo/tests/testsuite/run.rs
index aa210d6ae..586502288 100644
--- a/src/tools/cargo/tests/testsuite/run.rs
+++ b/src/tools/cargo/tests/testsuite/run.rs
@@ -777,14 +777,14 @@ fast2",
[COMPILING] bar v0.5.0 ([CWD]/bar)
[RUNNING] `rustc --crate-name bar bar/src/bar.rs [..]--crate-type lib \
--emit=[..]link[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [CWD]/target/debug/deps \
-L dependency=[CWD]/target/debug/deps`
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name a examples/a.rs [..]--crate-type bin \
--emit=[..]link[..]\
- -C debuginfo=2 \
+ -C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [CWD]/target/debug/examples \
-L dependency=[CWD]/target/debug/deps \
diff --git a/src/tools/cargo/tests/testsuite/rustc.rs b/src/tools/cargo/tests/testsuite/rustc.rs
index 65e0740f8..6e3d69cf7 100644
--- a/src/tools/cargo/tests/testsuite/rustc.rs
+++ b/src/tools/cargo/tests/testsuite/rustc.rs
@@ -18,7 +18,7 @@ fn build_lib_for_foo() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
@@ -40,7 +40,7 @@ fn lib() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C debug-assertions=off \
-C metadata=[..] \
--out-dir [..] \
@@ -63,12 +63,12 @@ fn build_main_and_allow_unstable_options() {
"\
[COMPILING] {name} v{version} ([CWD])
[RUNNING] `rustc --crate-name {name} src/lib.rs [..]--crate-type lib \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C metadata=[..] \
--out-dir [..] \
-L dependency=[CWD]/target/debug/deps`
[RUNNING] `rustc --crate-name {name} src/main.rs [..]--crate-type bin \
- --emit=[..]link[..]-C debuginfo=2 \
+ --emit=[..]link[..]-C debuginfo=2 [..]\
-C debug-assertions \
-C metadata=[..] \
--out-dir [..] \
@@ -109,10 +109,10 @@ fn build_with_args_to_one_of_multiple_binaries() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\
- -C debuginfo=2 -C metadata=[..] \
+ -C debuginfo=2 [..]-C metadata=[..] \
--out-dir [..]`
[RUNNING] `rustc --crate-name bar src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]\
- -C debuginfo=2 -C debug-assertions [..]`
+ -C debuginfo=2 [..]-C debug-assertions [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@@ -381,9 +381,9 @@ fn build_with_args_to_one_of_multiple_tests() {
"\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\
- -C debuginfo=2 -C metadata=[..] \
+ -C debuginfo=2 [..]-C metadata=[..] \
--out-dir [..]`
-[RUNNING] `rustc --crate-name bar tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 \
+[RUNNING] `rustc --crate-name bar tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 [..]\
-C debug-assertions [..]--test[..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
@@ -420,7 +420,7 @@ fn build_foo_with_bar_dependency() {
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `[..] -C debuginfo=2 [..]`
[COMPILING] foo v0.0.1 ([CWD])
-[RUNNING] `[..] -C debuginfo=2 -C debug-assertions [..]`
+[RUNNING] `[..] -C debuginfo=2 [..]-C debug-assertions [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@@ -478,7 +478,7 @@ fn targets_selected_default() {
// unit test
.with_stderr_does_not_contain(
"[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link \
- -C debuginfo=2 --test [..]",
+ -C debuginfo=2 [..]--test [..]",
)
.run();
}
@@ -495,7 +495,7 @@ fn targets_selected_all() {
// unit test
.with_stderr_contains(
"[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\
- -C debuginfo=2 --test [..]",
+ -C debuginfo=2 [..]--test [..]",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/script.rs b/src/tools/cargo/tests/testsuite/script.rs
new file mode 100644
index 000000000..fcf58de69
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/script.rs
@@ -0,0 +1,1184 @@
+use cargo_test_support::basic_manifest;
+use cargo_test_support::registry::Package;
+
+const ECHO_SCRIPT: &str = r#"#!/usr/bin/env cargo
+
+fn main() {
+ let mut args = std::env::args_os();
+ let bin = args.next().unwrap().to_str().unwrap().to_owned();
+ let args = args.collect::<Vec<_>>();
+ println!("bin: {bin}");
+ println!("args: {args:?}");
+}
+
+#[test]
+fn test () {}
+"#;
+
+#[cfg(unix)]
+fn path() -> Vec<std::path::PathBuf> {
+ std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default()).collect()
+}
+
+#[cargo_test]
+fn basic_rs() {
+ let p = cargo_test_support::project()
+ .file("echo.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript -v echo.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/echo[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] echo v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/echo[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn basic_path() {
+ let p = cargo_test_support::project()
+ .file("echo", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript -v ./echo")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/echo[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] echo v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/echo[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn basic_cargo_toml() {
+ let p = cargo_test_support::project()
+ .file("src/main.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript -v Cargo.toml")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: target/debug/foo[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `target/debug/foo[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn path_required() {
+ let p = cargo_test_support::project()
+ .file("echo", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript -v echo")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stdout("")
+ .with_stderr(
+ "\
+error: no such command: `echo`
+
+<tab>Did you mean `bench`?
+
+<tab>View all installed commands with `cargo --list`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+#[cfg(unix)]
+fn manifest_precedence_over_plugins() {
+ let p = cargo_test_support::project()
+ .file("echo.rs", ECHO_SCRIPT)
+ .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "")
+ .build();
+
+ // With path - fmt is there with known description
+ let mut path = path();
+ path.push(p.root().join("path-test"));
+ let path = std::env::join_paths(path.iter()).unwrap();
+
+ p.cargo("-Zscript -v echo.rs")
+ .env("PATH", &path)
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/echo[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] echo v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/echo[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+#[cfg(unix)]
+fn warn_when_plugin_masks_manifest_on_stable() {
+ let p = cargo_test_support::project()
+ .file("echo.rs", ECHO_SCRIPT)
+ .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "")
+ .build();
+
+ let mut path = path();
+ path.push(p.root().join("path-test"));
+ let path = std::env::join_paths(path.iter()).unwrap();
+
+ p.cargo("-v echo.rs")
+ .env("PATH", &path)
+ .with_stdout("")
+ .with_stderr(
+ "\
+warning: external subcommand `echo.rs` has the appearance of a manfiest-command
+This was previously accepted but will be phased out when `-Zscript` is stabilized.
+For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>.
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn requires_nightly() {
+ let p = cargo_test_support::project()
+ .file("echo.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-v echo.rs")
+ .with_status(101)
+ .with_stdout("")
+ .with_stderr(
+ "\
+error: running `echo.rs` requires `-Zscript`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn requires_z_flag() {
+ let p = cargo_test_support::project()
+ .file("echo.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-v echo.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stdout("")
+ .with_stderr(
+ "\
+error: running `echo.rs` requires `-Zscript`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_output_with_edition() {
+ let script = r#"#!/usr/bin/env cargo
+
+//! ```cargo
+//! [package]
+//! edition = "2018"
+//! ```
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn warning_without_edition() {
+ let script = r#"#!/usr/bin/env cargo
+
+//! ```cargo
+//! [package]
+//! ```
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn rebuild() {
+ let script = r#"#!/usr/bin/env cargo-eval
+
+fn main() {
+ let msg = option_env!("_MESSAGE").unwrap_or("undefined");
+ println!("msg = {}", msg);
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"msg = undefined
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+
+ // Verify we don't rebuild
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"msg = undefined
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+
+ // Verify we do rebuild
+ p.cargo("-Zscript -v script.rs")
+ .env("_MESSAGE", "hello")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"msg = hello
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn use_script_config() {
+ let script = ECHO_SCRIPT;
+ let _ = cargo_test_support::project()
+ .at("script")
+ .file("script.rs", script)
+ .build();
+
+ let p = cargo_test_support::project()
+ .file(
+ ".cargo/config",
+ r#"
+[build]
+rustc = "non-existent-rustc"
+"#,
+ )
+ .file("script.rs", script)
+ .build();
+
+ // Verify the config is bad
+ p.cargo("-Zscript script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[ERROR] could not execute process `non-existent-rustc -vV` (never executed)
+",
+ )
+ .run();
+
+ // Verify that the config isn't used
+ p.cargo("-Zscript ../script/script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["-NotAnArg"]
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn default_programmatic_verbosity() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["-NotAnArg"]
+"#,
+ )
+ .with_stderr(
+ "\
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn quiet() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -q script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["-NotAnArg"]
+"#,
+ )
+ .with_stderr(
+ "\
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_line_numbering_preserved() {
+ let script = r#"#!/usr/bin/env cargo
+
+fn main() {
+ println!("line: {}", line!());
+}
+"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"line: 4
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_escaped_hyphen_arg() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v -- script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["-NotAnArg"]
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] -NotAnArg`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_unescaped_hyphen_arg() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs -NotAnArg")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["-NotAnArg"]
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] -NotAnArg`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_same_flags() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs --help")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: ["--help"]
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] --help`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_name_has_weird_chars() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("s-h.w§c!.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v s-h.w§c!.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/s-h-w-c-[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ r#"[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] s-h-w-c- v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/s-h-w-c-[EXE]`
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn script_like_dir() {
+ let p = cargo_test_support::project()
+ .file("script.rs/foo", "something")
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr(
+ "\
+error: manifest path `script.rs` is a directory but expected a file
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn missing_script_rs() {
+ let p = cargo_test_support::project().build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] manifest path `script.rs` does not exist
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_name_same_as_dependency() {
+ Package::new("script", "1.0.0").publish();
+ let script = r#"#!/usr/bin/env cargo
+
+//! ```cargo
+//! [dependencies]
+//! script = "1.0.0"
+//! ```
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs --help")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[UPDATING] `dummy-registry` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] script v1.0.0 (registry `dummy-registry`)
+[COMPILING] script v1.0.0
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] --help`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_path_dep() {
+ let script = r#"#!/usr/bin/env cargo
+
+//! ```cargo
+//! [dependencies]
+//! bar.path = "./bar"
+//! ```
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .file("src/lib.rs", "pub fn foo() {}")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .build();
+
+ p.cargo("-Zscript -v script.rs --help")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] bar v0.0.1 ([ROOT]/foo/bar)
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] --help`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_no_build_rs() {
+ let script = r#"#!/usr/bin/env cargo
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .file("build.rs", "broken")
+ .build();
+
+ p.cargo("-Zscript -v script.rs --help")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] --help`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_no_autobins() {
+ let script = r#"#!/usr/bin/env cargo
+
+fn main() {
+ println!("Hello world!");
+}"#;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .file("src/bin/not-script/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("-Zscript -v script.rs --help")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"Hello world!
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE] --help`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn implicit_target_dir() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [ROOT]/home/.cargo/target/[..]/debug/script[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn no_local_lockfile() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+ let local_lockfile_path = p.root().join("Cargo.lock");
+
+ assert!(!local_lockfile_path.exists());
+
+ p.cargo("-Zscript -v script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [ROOT]/home/.cargo/target/[..]/debug/script[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]`
+",
+ )
+ .run();
+
+ assert!(!local_lockfile_path.exists());
+}
+
+#[cargo_test]
+fn cmd_check_requires_nightly() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("check --manifest-path script.rs")
+ .with_status(101)
+ .with_stdout("")
+ .with_stderr(
+ "\
+error: embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_check_requires_z_flag() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("check --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stdout("")
+ .with_stderr(
+ "\
+error: embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_check_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript check --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[CHECKING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_check_with_missing_script_rs() {
+ let p = cargo_test_support::project().build();
+
+ p.cargo("-Zscript check --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[ERROR] manifest path `script.rs` does not exist
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_check_with_missing_script() {
+ let p = cargo_test_support::project().build();
+
+ p.cargo("-Zscript check --manifest-path script")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[ERROR] the manifest-path must be a path to a Cargo.toml file
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_build_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript build --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_test_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript test --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [..]s
+
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] unittests script.rs ([..])
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_clean_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ // Ensure there is something to clean
+ p.cargo("-Zscript script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .run();
+
+ p.cargo("-Zscript clean --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_generate_lockfile_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript generate-lockfile --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_metadata_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript metadata --manifest-path script.rs --format-version=1")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_json(
+ r#"
+ {
+ "packages": [
+ {
+ "authors": [
+ ],
+ "categories": [],
+ "default_run": null,
+ "name": "script",
+ "version": "0.0.0",
+ "id": "script[..]",
+ "keywords": [],
+ "source": null,
+ "dependencies": [],
+ "edition": "[..]",
+ "license": null,
+ "license_file": null,
+ "links": null,
+ "description": null,
+ "readme": null,
+ "repository": null,
+ "rust_version": null,
+ "homepage": null,
+ "documentation": null,
+ "homepage": null,
+ "documentation": null,
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "doc": true,
+ "doctest": false,
+ "test": true,
+ "edition": "[..]",
+ "name": "script",
+ "src_path": "[..]/script.rs"
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]script.rs",
+ "metadata": null,
+ "publish": []
+ }
+ ],
+ "workspace_members": ["script 0.0.0 (path+file:[..]foo)"],
+ "workspace_default_members": ["script 0.0.0 (path+file:[..]foo)"],
+ "resolve": {
+ "nodes": [
+ {
+ "dependencies": [],
+ "deps": [],
+ "features": [],
+ "id": "script 0.0.0 (path+file:[..]foo)"
+ }
+ ],
+ "root": "script 0.0.0 (path+file:[..]foo)"
+ },
+ "target_directory": "[ROOT]/home/.cargo/target/[..]",
+ "version": 1,
+ "workspace_root": "[..]/foo",
+ "metadata": null
+ }"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_read_manifest_with_embedded() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("script.rs", script)
+ .build();
+
+ p.cargo("-Zscript read-manifest --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_json(
+ r#"
+{
+ "authors": [
+ ],
+ "categories": [],
+ "default_run": null,
+ "name":"script",
+ "readme": null,
+ "homepage": null,
+ "documentation": null,
+ "repository": null,
+ "rust_version": null,
+ "version":"0.0.0",
+ "id":"script[..]0.0.0[..](path+file://[..]/foo)",
+ "keywords": [],
+ "license": null,
+ "license_file": null,
+ "links": null,
+ "description": null,
+ "edition": "[..]",
+ "source":null,
+ "dependencies":[],
+ "targets":[{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "doc": true,
+ "doctest": false,
+ "test": true,
+ "edition": "[..]",
+ "name":"script",
+ "src_path":"[..]/script.rs"
+ }],
+ "features":{},
+ "manifest_path":"[..]script.rs",
+ "metadata": null,
+ "publish": []
+}"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_run_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript run --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/script[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[COMPILING] script v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/script[EXE]`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_tree_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript tree --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+script v0.0.0 ([ROOT]/foo)
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_update_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript update --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ "\
+",
+ )
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_verify_project_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript verify-project --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_json(r#"{"success":"true"}"#)
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/test.rs b/src/tools/cargo/tests/testsuite/test.rs
index add0a991f..6a062cfb6 100644
--- a/src/tools/cargo/tests/testsuite/test.rs
+++ b/src/tools/cargo/tests/testsuite/test.rs
@@ -387,8 +387,9 @@ test test_hello ... FAILED
failures:
---- test_hello stdout ----
-[..]thread '[..]' panicked at 'assertion failed:[..]",
+[..]thread '[..]' panicked at [..]",
)
+ .with_stdout_contains("[..]assertion failed[..]")
.with_stdout_contains("[..]`(left == right)`[..]")
.with_stdout_contains("[..]left: `\"hello\"`,[..]")
.with_stdout_contains("[..]right: `\"nope\"`[..]")
@@ -437,10 +438,10 @@ test test_hello ... FAILED
failures:
---- test_hello stdout ----
-[..]thread '[..]' panicked at 'assertion failed: false', \
- tests/footest.rs:1[..]
+[..]thread '[..]' panicked at [..]tests/footest.rs:1:[..]
",
)
+ .with_stdout_contains("[..]assertion failed[..]")
.with_stdout_contains(
"\
failures:
@@ -473,10 +474,10 @@ test test_hello ... FAILED
failures:
---- test_hello stdout ----
-[..]thread '[..]' panicked at 'assertion failed: false', \
- src/lib.rs:1[..]
+[..]thread '[..]' panicked at [..]src/lib.rs:1:[..]
",
)
+ .with_stdout_contains("[..]assertion failed[..]")
.with_stdout_contains(
"\
failures:
diff --git a/src/tools/cargo/triagebot.toml b/src/tools/cargo/triagebot.toml
index 00a98e008..6d07f438f 100644
--- a/src/tools/cargo/triagebot.toml
+++ b/src/tools/cargo/triagebot.toml
@@ -137,7 +137,7 @@ trigger_files = [
]
[autolabel."A-interacts-with-crates.io"]
-trigger_files = ["crates/crates-io/", "src/cargo/ops/registry.rs"]
+trigger_files = ["crates/crates-io/", "src/cargo/ops/registry/"]
[autolabel."A-layout"]
trigger_files = [
@@ -180,7 +180,7 @@ trigger_files = ["src/cargo/core/compiler/fingerprint/"]
trigger_files = ["src/cargo/sources/registry/", "src/cargo/core/registry.rs"]
[autolabel."A-registry-authentication"]
-trigger_files = ["src/cargo/util/auth.rs", "credential/"]
+trigger_files = ["src/cargo/util/auth/", "credential/"]
[autolabel."A-semver"]
trigger_files = [
@@ -266,10 +266,10 @@ trigger_files = ["src/bin/cargo/commands/install.rs", "src/cargo/ops/cargo_insta
trigger_files = ["src/bin/cargo/commands/locate_project.rs"]
[autolabel."Command-login"]
-trigger_files = ["src/bin/cargo/commands/login.rs"]
+trigger_files = ["src/bin/cargo/commands/login.rs", "src/cargo/ops/registry/login.rs"]
[autolabel."Command-logout"]
-trigger_files = ["src/bin/cargo/commands/logout.rs"]
+trigger_files = ["src/bin/cargo/commands/logout.rs", "src/cargo/ops/registry/logout.rs"]
[autolabel."Command-metadata"]
trigger_files = ["src/bin/cargo/commands/metadata.rs", "src/cargo/ops/cargo_output_metadata.rs"]
@@ -278,7 +278,7 @@ trigger_files = ["src/bin/cargo/commands/metadata.rs", "src/cargo/ops/cargo_outp
trigger_files = ["src/bin/cargo/commands/new.rs", "src/cargo/ops/cargo_new.rs"]
[autolabel."Command-owner"]
-trigger_files = ["src/bin/cargo/commands/owner.rs"]
+trigger_files = ["src/bin/cargo/commands/owner.rs", "src/cargo/ops/registry/owner.rs"]
[autolabel."Command-package"]
trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_package.rs"]
@@ -287,7 +287,7 @@ trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_packa
trigger_files = ["src/bin/cargo/commands/pkgid.rs", "src/cargo/ops/cargo_pkgid.rs"]
[autolabel."Command-publish"]
-trigger_files = ["src/bin/cargo/commands/publish.rs"]
+trigger_files = ["src/bin/cargo/commands/publish.rs", "src/cargo/ops/registry/publish.rs"]
[autolabel."Command-read-manifest"]
trigger_files = ["src/bin/cargo/commands/read_manifest.rs", "src/cargo/ops/cargo_read_manifest.rs"]
@@ -308,7 +308,7 @@ trigger_files = ["src/bin/cargo/commands/rustc.rs"]
trigger_files = ["src/bin/cargo/commands/rustdoc.rs"]
[autolabel."Command-search"]
-trigger_files = ["src/bin/cargo/commands/search.rs"]
+trigger_files = ["src/bin/cargo/commands/search.rs", "src/cargo/ops/registry/search.rs"]
[autolabel."Command-test"]
trigger_files = ["src/bin/cargo/commands/test.rs", "src/cargo/ops/cargo_test.rs"]
@@ -332,4 +332,4 @@ trigger_files = ["src/bin/cargo/commands/verify_project.rs"]
trigger_files = ["src/bin/cargo/commands/version.rs"]
[autolabel."Command-yank"]
-trigger_files = ["src/bin/cargo/commands/yank.rs"]
+trigger_files = ["src/bin/cargo/commands/yank.rs", "src/cargo/ops/registry/yank.rs"]
diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml
index 4d80d3ce6..48a63e485 100644
--- a/src/tools/clippy/.cargo/config.toml
+++ b/src/tools/clippy/.cargo/config.toml
@@ -1,5 +1,7 @@
[alias]
uitest = "test --test compile-test"
+uibless = "test --test compile-test -- -- --bless"
+bless = "test -- -- --bless"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
index 0b43d8d70..b49493edc 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
@@ -12,29 +12,6 @@ body:
description: What does this lint do?
validations:
required: true
- - type: input
- id: lint-name
- attributes:
- label: Lint Name
- description: Please provide the lint name.
- - type: dropdown
- id: category
- attributes:
- label: Category
- description: >
- What category should this lint go into? If you're unsure you can select
- multiple categories. You can find a category description in the
- `README`.
- multiple: true
- options:
- - correctness
- - suspicious
- - style
- - complexity
- - perf
- - pedantic
- - restriction
- - cargo
- type: textarea
id: advantage
attributes:
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
index a9d42159c..c582c28cd 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy.yml
@@ -25,7 +25,6 @@ env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
- CARGO_UNSTABLE_SPARSE_REGISTRY: true
jobs:
base:
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 30a156c92..d5ab313ba 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -11,7 +11,6 @@ env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
- CARGO_UNSTABLE_SPARSE_REGISTRY: true
defaults:
run:
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index 514706d64..0f0e3f2db 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -16,7 +16,6 @@ on:
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
- CARGO_UNSTABLE_SPARSE_REGISTRY: true
jobs:
clippy_dev:
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 79f2a4711..14d822083 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,13 +6,138 @@ document.
## Unreleased / Beta / In Rust Nightly
-[149392b0...master](https://github.com/rust-lang/rust-clippy/compare/149392b0...master)
+[83e42a23...master](https://github.com/rust-lang/rust-clippy/compare/83e42a23...master)
+
+## Rust 1.70
+
+Current stable, released 2023-06-01
+
+[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
+
+### New Lints
+
+* [`large_futures`]
+ [#10414](https://github.com/rust-lang/rust-clippy/pull/10414)
+* [`missing_assert_message`]
+ [#10362](https://github.com/rust-lang/rust-clippy/pull/10362)
+* [`clear_with_drain`]
+ [#10528](https://github.com/rust-lang/rust-clippy/pull/10528)
+* [`redundant_async_block`]
+ [#10448](https://github.com/rust-lang/rust-clippy/pull/10448)
+* [`collection_is_never_read`]
+ [#10415](https://github.com/rust-lang/rust-clippy/pull/10415)
+* [`let_with_type_underscore`]
+ [#10467](https://github.com/rust-lang/rust-clippy/pull/10467)
+* [`tests_outside_test_module`]
+ [#10543](https://github.com/rust-lang/rust-clippy/pull/10543)
+* [`allow_attributes`]
+ [#10481](https://github.com/rust-lang/rust-clippy/pull/10481)
+* [`suspicious_doc_comments`]
+ [#10497](https://github.com/rust-lang/rust-clippy/pull/10497)
+* [`unnecessary_box_returns`]
+ [#9102](https://github.com/rust-lang/rust-clippy/pull/9102)
+* [`manual_main_separator_str`]
+ [#10483](https://github.com/rust-lang/rust-clippy/pull/10483)
+* [`unnecessary_struct_initialization`]
+ [#10489](https://github.com/rust-lang/rust-clippy/pull/10489)
+* [`manual_slice_size_calculation`]
+ [#10601](https://github.com/rust-lang/rust-clippy/pull/10601)
+* [`lines_filter_map_ok`]
+ [#10534](https://github.com/rust-lang/rust-clippy/pull/10534)
+
+### Moves and Deprecations
+
+* Moved [`let_underscore_untyped`] to `restriction`
+ [#10442](https://github.com/rust-lang/rust-clippy/pull/10442)
+
+### Enhancements
+
+* [`extra_unused_type_parameters`]: No longer lints on public items if `avoid-breaking-exported-api` is set
+ [#10536](https://github.com/rust-lang/rust-clippy/pull/10536)
+* [`len_without_is_empty`]: Now also detects `async` functions
+ [#10359](https://github.com/rust-lang/rust-clippy/pull/10359)
+* [`arithmetic_side_effects`]: Now correctly handles divisions and modulo expressions if the right-hand-side
+ is unknown
+ [#10585](https://github.com/rust-lang/rust-clippy/pull/10585)
+* [`nonminimal_bool`]: No longer ignores `#[allow]` attributes
+ [#10588](https://github.com/rust-lang/rust-clippy/pull/10588)
+* [`uninit_vec`], [`uninit_assumed_init`]: Now uses a better heuristic
+ [#10520](https://github.com/rust-lang/rust-clippy/pull/10520)
+* [`ifs_same_cond`]: Now also detects immutable method calls.
+ [#10350](https://github.com/rust-lang/rust-clippy/pull/10350)
+* [`arithmetic_side_effects`]: No longer lints on right or left shifts with constant integers, as the
+ compiler warns about them
+ [#10309](https://github.com/rust-lang/rust-clippy/pull/10309)
+* [`items_after_statements`]: `#[allow(items_after_statements)]` now works on items
+ [#10542](https://github.com/rust-lang/rust-clippy/pull/10542)
+* [`significant_drop_tightening`]: Was optimized
+ [#10533](https://github.com/rust-lang/rust-clippy/pull/10533)
+
+### False Positive Fixes
+
+* [`single_component_path_imports`]: No longer lints if the import is used relative to `self`
+ [#10566](https://github.com/rust-lang/rust-clippy/pull/10566)
+* [`derivable_impls`]: No longer suggests deriving `Default` on generics with implicit arguments
+ [#10399](https://github.com/rust-lang/rust-clippy/pull/10399)
+* [`let_unit_value`]: No longer lints if the expression contains an `await`
+ [#10439](https://github.com/rust-lang/rust-clippy/pull/10439)
+* [`double_must_use`]: Now ignores `async` functions
+ [#10589](https://github.com/rust-lang/rust-clippy/pull/10589)
+* [`manual_clamp`]: No longer lints in constant context
+ [#10479](https://github.com/rust-lang/rust-clippy/pull/10479)
+* [`almost_swapped`]: Now ignores external macros
+ [#10502](https://github.com/rust-lang/rust-clippy/pull/10502)
+* [`nonminimal_bool`]: Now ignores macros
+ [#10527](https://github.com/rust-lang/rust-clippy/pull/10527)
+* [`needless_return`]: No longer lints match statements with incompatible branches
+ [#10593](https://github.com/rust-lang/rust-clippy/pull/10593)
+* [`use_self`]: Do not suggest using `Self` in const generic parameters
+ [#10375](https://github.com/rust-lang/rust-clippy/pull/10375)
+* [`mem_replace_option_with_none`]: No longer lints on field expressions
+ [#10594](https://github.com/rust-lang/rust-clippy/pull/10594)
+* [`items_after_statements`]: No longer lints on items from macros
+ [#10542](https://github.com/rust-lang/rust-clippy/pull/10542)
+* [`print_literal`], [`write_literal`]: No longer lint strings coming from the `file!()` macro
+ [#10573](https://github.com/rust-lang/rust-clippy/pull/10573)
+* [`uninit_vec`], [`uninit_assumed_init`]: Now check the types inside arrays and tuples
+ [#10553](https://github.com/rust-lang/rust-clippy/pull/10553)
+* [`almost_swapped`]: No longer lints if a variable is assigned to itself
+ [#10499](https://github.com/rust-lang/rust-clippy/pull/10499)
+* [`missing_docs_in_private_items`]: No longer lints on public items
+ [#10324](https://github.com/rust-lang/rust-clippy/pull/10324)
+
+### Suggestion Fixes/Improvements
+
+* [`extra_unused_type_parameters`]: The suggestion is now machine applicable
+ [#10536](https://github.com/rust-lang/rust-clippy/pull/10536)
+* [`match_single_binding`]: Now adds a semicolon after the suggestion
+ [#10470](https://github.com/rust-lang/rust-clippy/pull/10470)
+* [`missing_const_for_fn`]: Now includes a note if the change could break compatibility
+ [#10618](https://github.com/rust-lang/rust-clippy/pull/10618)
+* [`cast_possible_truncation`]: Corrected suggestion for float and wildcard casts
+ [#10496](https://github.com/rust-lang/rust-clippy/pull/10496)
+* [`transmutes_expressible_as_ptr_casts`]: The suggestion now includes parentheses when they are required
+ [#10454](https://github.com/rust-lang/rust-clippy/pull/10454)
+
+### ICE Fixes
+
+* [`needless_borrow`]: No longer panics on ambiguous projections
+ [#10403](https://github.com/rust-lang/rust-clippy/pull/10403)
+* [`multiple_unsafe_ops_per_block`]: Fix ICE when calling a function-like object in an unsafe block
+ [#10405](https://github.com/rust-lang/rust-clippy/pull/10405)
+
+### Others
+
+* `clippy-driver` now searches parent directories for `clippy.toml` files
+ [#10592](https://github.com/rust-lang/rust-clippy/pull/10592)
+* Fixed a deserialization error for the `array-size-threshold` config value
+ [#10423](https://github.com/rust-lang/rust-clippy/pull/10423)
## Rust 1.69
-Current stable, released 2023-04-20
+Released 2023-04-20
-[7f27e2e7...149392b0](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...149392b0)
+[**View 86 PRs merged since 1.68**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-03-09..2023-04-20+base%3Amaster+sort%3Amerged-desc+)
### New Lints
@@ -127,7 +252,7 @@ Current stable, released 2023-04-20
Released 2023-03-09
-[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
+[**View 85 PRs merged since 1.67**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-01-26..2023-03-09+base%3Amaster+sort%3Amerged-desc+)
### New Lints
@@ -274,7 +399,7 @@ Released 2023-03-09
Released 2023-01-26
-[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
+[**View 68 PRs merged since 1.66**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-12-15..2023-01-26+base%3Amaster+sort%3Amerged-desc+)
### New Lints
@@ -465,7 +590,8 @@ Released 2023-01-26
Released 2022-12-15
-[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1)
+[**View 93 PRs merged since 1.65**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-11-03..2022-12-15+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -636,7 +762,8 @@ Released 2022-12-15
Released 2022-11-03
-[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523)
+[**View 129 PRs merged since 1.64**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-09-22..2022-11-03+base%3Amaster+sort%3Amerged-desc+)
+
### Important Changes
@@ -780,7 +907,8 @@ Released 2022-11-03
Released 2022-09-22
-[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
+[**View 92 PRs merged since 1.63**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-08-11..2022-09-22+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -930,7 +1058,8 @@ Released 2022-09-22
Released 2022-08-11
-[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
+[**View 100 PRs merged since 1.62**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-06-30..2022-08-11+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1076,7 +1205,8 @@ Released 2022-08-11
Released 2022-06-30
-[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
+[**View 104 PRs merged since 1.61**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-05-19..2022-06-30+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1233,7 +1363,8 @@ Released 2022-06-30
Released 2022-05-19
-[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
+[**View 93 PRs merged since 1.60**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-04-07..2022-05-19+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1334,7 +1465,8 @@ Released 2022-05-19
Released 2022-04-07
-[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
+[**View 75 PRs merged since 1.59**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-02-24..2022-04-07+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1466,7 +1598,8 @@ Released 2022-04-07
Released 2022-02-24
-[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
+[**View 63 PRs merged since 1.58**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-01-13..2022-02-24+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1630,7 +1763,8 @@ Released 2022-02-24
Released 2022-01-13
-[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
+[**View 73 PRs merged since 1.57**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-12-02..2022-01-13+base%3Amaster+sort%3Amerged-desc+)
+
### Rust 1.58.1
@@ -1751,7 +1885,8 @@ Released 2022-01-13
Released 2021-12-02
-[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
+[**View 92 PRs merged since 1.56**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-10-21..2021-12-02+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -1902,7 +2037,7 @@ Released 2021-12-02
Released 2021-10-21
-[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
+[**View 92 PRs merged since 1.55**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-09-09..2021-10-21+base%3Amaster+sort%3Amerged-desc+)
### New Lints
@@ -1968,7 +2103,7 @@ Released 2021-10-21
Released 2021-09-09
-[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
+[**View 61 PRs merged since 1.54**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-07-29..2021-09-09+base%3Amaster+sort%3Amerged-desc+)
### Important Changes
@@ -2086,7 +2221,8 @@ Released 2021-09-09
Released 2021-07-29
-[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
+[**View 68 PRs merged since 1.53**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-06-17..2021-07-29+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -2214,7 +2350,7 @@ Released 2021-07-29
Released 2021-06-17
-[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
+[**View 80 PRs merged since 1.52**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-05-06..2021-06-17+base%3Amaster+sort%3Amerged-desc+)
### New Lints
@@ -2398,7 +2534,8 @@ Released 2021-06-17
Released 2021-05-06
-[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
+[**View 113 PRs merged since 1.51**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-03-25..2021-05-06+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -2533,7 +2670,8 @@ Released 2021-05-06
Released 2021-03-25
-[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
+[**View 117 PRs merged since 1.50**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-02-11..2021-03-25+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -2648,7 +2786,8 @@ Released 2021-03-25
Released 2021-02-11
-[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
+[**View 90 PRs merged since 1.49**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-12-31..2021-02-11+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -2777,7 +2916,8 @@ Released 2021-02-11
Released 2020-12-31
-[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
+[**View 85 PRs merged since 1.48**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-11-19..2020-12-31+base%3Amaster+sort%3Amerged-desc+)
+
### New Lints
@@ -2883,7 +3023,7 @@ Released 2020-12-31
Released 2020-11-19
-[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
+[**View 112 PRs merged since 1.47**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-10-08..2020-11-19+base%3Amaster+sort%3Amerged-desc+)
### New lints
@@ -3001,7 +3141,8 @@ Released 2020-11-19
Released 2020-10-08
-[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
+[**View 80 PRs merged since 1.46**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-08-27..2020-10-08+base%3Amaster+sort%3Amerged-desc+)
+
### New lints
@@ -3103,7 +3244,8 @@ Released 2020-10-08
Released 2020-08-27
-[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
+[**View 93 PRs merged since 1.45**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-07-16..2020-08-27+base%3Amaster+sort%3Amerged-desc+)
+
### New lints
@@ -3165,7 +3307,8 @@ Released 2020-08-27
Released 2020-07-16
-[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
+[**View 65 PRs merged since 1.44**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-06-04..2020-07-16+base%3Amaster+sort%3Amerged-desc+)
+
### New lints
@@ -3242,7 +3385,8 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565
Released 2020-06-04
-[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
+[**View 88 PRs merged since 1.43**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-04-23..2020-06-04+base%3Amaster+sort%3Amerged-desc+)
+
### New lints
@@ -3325,7 +3469,8 @@ Released 2020-06-04
Released 2020-04-23
-[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
+[**View 121 PRs merged since 1.42**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-03-12..2020-04-23+base%3Amaster+sort%3Amerged-desc+)
+
### New lints
@@ -3383,7 +3528,7 @@ Released 2020-04-23
Released 2020-03-12
-[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
+[**View 106 PRs merged since 1.41**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-01-30..2020-03-12+base%3Amaster+sort%3Amerged-desc+)
### New lints
@@ -3450,7 +3595,7 @@ Released 2020-03-12
Released 2020-01-30
-[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
+[**View 107 PRs merged since 1.40**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-12-19..2020-01-30+base%3Amaster+sort%3Amerged-desc+)
* New Lints:
* [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
@@ -3495,7 +3640,8 @@ Released 2020-01-30
Released 2019-12-19
-[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
+[**View 69 😺 PRs merged since 1.39**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-11-07..2019-12-19+base%3Amaster+sort%3Amerged-desc+)
+
* New Lints:
* [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
@@ -3537,7 +3683,7 @@ Released 2019-12-19
Released 2019-11-07
-[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
+[**View 84 PRs merged since 1.38**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-09-26..2019-11-07+base%3Amaster+sort%3Amerged-desc+)
* New Lints:
* [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
@@ -3581,7 +3727,7 @@ Released 2019-11-07
Released 2019-09-26
-[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
+[**View 102 PRs merged since 1.37**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-08-15..2019-09-26+base%3Amaster+sort%3Amerged-desc+)
* New Lints:
* [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
@@ -3611,7 +3757,7 @@ Released 2019-09-26
Released 2019-08-15
-[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
+[**View 83 PRs merged since 1.36**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-07-04..2019-08-15+base%3Amaster+sort%3Amerged-desc+)
* New Lints:
* [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
@@ -3635,7 +3781,8 @@ Released 2019-08-15
Released 2019-07-04
-[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
+[**View 75 PRs merged since 1.35**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-05-20..2019-07-04+base%3Amaster+sort%3Amerged-desc+)
+
* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
@@ -3666,7 +3813,8 @@ Released 2019-07-04
Released 2019-05-20
-[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
+[**View 90 PRs merged since 1.34**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-04-10..2019-05-20+base%3Amaster+sort%3Amerged-desc+)
+
* New lint: `drop_bounds` to detect `T: Drop` bounds
* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
@@ -3694,7 +3842,8 @@ Released 2019-05-20
Released 2019-04-10
-[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
+[**View 66 PRs merged since 1.33**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-02-26..2019-04-10+base%3Amaster+sort%3Amerged-desc+)
+
* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
@@ -3724,7 +3873,7 @@ Released 2019-04-10
Released 2019-02-26
-[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
+[**View 83 PRs merged since 1.32**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-01-17..2019-02-26+base%3Amaster+sort%3Amerged-desc+)
* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
* The `rust-clippy` repository is now part of the `rust-lang` org.
@@ -3757,7 +3906,7 @@ Released 2019-02-26
Released 2019-01-17
-[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
+[**View 106 PRs merged since 1.31**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-12-06..2019-01-17+base%3Amaster+sort%3Amerged-desc+)
* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
[`redundant_clone`], [`wildcard_dependencies`],
@@ -3787,7 +3936,8 @@ Released 2019-01-17
Released 2018-12-06
-[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
+[**View 85 PRs merged since 1.30**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-10-25..2018-12-06+base%3Amaster+sort%3Amerged-desc+)
+
* Clippy has been relicensed under a dual MIT / Apache license.
See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
@@ -3827,7 +3977,8 @@ Released 2018-12-06
Released 2018-10-25
-[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
+[**View 106 PRs merged since 1.29**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-09-13..2018-10-25+base%3Amaster+sort%3Amerged-desc+)
+
* Deprecate `assign_ops` lint
* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
@@ -4503,6 +4654,7 @@ Released 2018-09-13
[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
+[`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync
[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
@@ -4516,6 +4668,7 @@ Released 2018-09-13
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`big_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#big_endian_bytes
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
@@ -4610,6 +4763,7 @@ Released 2018-09-13
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
+[`drain_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#drain_collect
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
@@ -4632,6 +4786,7 @@ Released 2018-09-13
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
+[`excessive_nesting`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
@@ -4686,6 +4841,7 @@ Released 2018-09-13
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
+[`host_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#host_endian_bytes
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
@@ -4704,6 +4860,7 @@ Released 2018-09-13
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type
[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
@@ -4754,6 +4911,7 @@ Released 2018-09-13
[`large_futures`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_futures
[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+[`large_stack_frames`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
@@ -4767,6 +4925,7 @@ Released 2018-09-13
[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
+[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
@@ -4790,6 +4949,7 @@ Released 2018-09-13
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
+[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
@@ -4799,6 +4959,7 @@ Released 2018-09-13
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
+[`manual_try_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
@@ -4822,11 +4983,13 @@ Released 2018-09-13
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
+[`maybe_misused_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_misused_cfg
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
+[`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
@@ -4838,6 +5001,7 @@ Released 2018-09-13
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
+[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
@@ -4874,7 +5038,9 @@ Released 2018-09-13
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
+[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
@@ -4882,8 +5048,11 @@ Released 2018-09-13
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
+[`needless_pub_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pub_self
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
+[`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes
+[`needless_raw_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_strings
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
@@ -4949,10 +5118,13 @@ Released 2018-09-13
[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
+[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
+[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
+[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
[`question_mark_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark_used
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
@@ -4966,6 +5138,7 @@ Released 2018-09-13
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
+[`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
@@ -4978,6 +5151,7 @@ Released 2018-09-13
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
@@ -5018,6 +5192,7 @@ Released 2018-09-13
[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
+[`single_call_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
@@ -5026,6 +5201,7 @@ Released 2018-09-13
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
+[`single_range_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_range_in_vec_init
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
@@ -5088,6 +5264,7 @@ Released 2018-09-13
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
+[`tuple_array_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
@@ -5110,6 +5287,7 @@ Released 2018-09-13
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
+[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
@@ -5183,3 +5361,65 @@ Released 2018-09-13
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
<!-- end autogenerated links to lint list -->
+<!-- begin autogenerated links to configuration documentation -->
+[`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed
+[`arithmetic-side-effects-allowed-binary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-binary
+[`arithmetic-side-effects-allowed-unary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-unary
+[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
+[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
+[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
+[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold
+[`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
+[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
+[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline
+[`doc-valid-idents`]: https://doc.rust-lang.org/clippy/lint_configuration.html#doc-valid-idents
+[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
+[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold
+[`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
+[`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
+[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
+[`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
+[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
+[`array-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#array-size-threshold
+[`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold
+[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
+[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds
+[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools
+[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
+[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
+[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
+[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods
+[`disallowed-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-types
+[`unreadable-literal-lint-fractions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unreadable-literal-lint-fractions
+[`upper-case-acronyms-aggressive`]: https://doc.rust-lang.org/clippy/lint_configuration.html#upper-case-acronyms-aggressive
+[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
+[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
+[`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
+[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames
+[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
+[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send
+[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length
+[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
+[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size
+[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
+[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
+[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
+[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
+[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
+[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
+[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
+[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
+[`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items
+[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
+[`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size
+[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
+[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
+[`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold
+[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
+[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes
+[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
+<!-- end autogenerated links to configuration documentation -->
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 3c72bb62e..76c804f93 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy"
-version = "0.1.71"
+version = "0.1.72"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -27,32 +27,14 @@ tempfile = { version = "3.2", optional = true }
termize = "0.1"
[dev-dependencies]
-compiletest_rs = { version = "0.10", features = ["tmp"] }
+ui_test = "0.11.5"
tester = "0.9"
regex = "1.5"
-toml = "0.5"
+toml = "0.7.3"
walkdir = "2.3"
# This is used by the `collect-metadata` alias.
filetime = "0.2"
-
-# A noop dependency that changes in the Rust repository, it's a bit of a hack.
-# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
-# for more information.
-rustc-workspace-hack = "1.0"
-
-# UI test dependencies
-clap = { version = "4.1.4", features = ["derive"] }
-clippy_utils = { path = "clippy_utils" }
-derive-new = "0.5"
-if_chain = "1.0"
itertools = "0.10.1"
-quote = "1.0"
-serde = { version = "1.0.125", features = ["derive"] }
-syn = { version = "2.0", features = ["full"] }
-futures = "0.3"
-parking_lot = "0.12"
-tokio = { version = "1", features = ["io-util"] }
-rustc-semver = "1.1"
[build-dependencies]
rustc_tools_util = "0.3.0"
@@ -65,3 +47,7 @@ internal = ["clippy_lints/internal", "tempfile"]
[package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)]
rustc_private = true
+
+[[test]]
+name = "compile-test"
+harness = false
diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md
index 1304f6a8c..e8274bc45 100644
--- a/src/tools/clippy/book/src/configuration.md
+++ b/src/tools/clippy/book/src/configuration.md
@@ -2,8 +2,14 @@
> **Note:** The configuration file is unstable and may be deprecated in the future.
-Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
-basic `variable = value` mapping e.g.
+Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in:
+
+1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or
+2. The directory specified by the
+[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or
+3. The current directory.
+
+It contains a basic `variable = value` mapping e.g.
```toml
avoid-breaking-exported-api = false
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index ccae8d374..a0db80892 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -122,20 +122,17 @@ fn main() {
}
```
-Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
+Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently
this test is meaningless though.
While we are working on implementing our lint, we can keep running the UI test.
-That allows us to check if the output is turning into what we want.
+That allows us to check if the output is turning into what we want by checking the
+`.stderr` file that gets updated on every test run.
-Once we are satisfied with the output, we need to run `cargo dev bless` to
-update the `.stderr` file for our lint. Please note that, we should run
-`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
-bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
+Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we
commit our lint, we need to commit the generated `.stderr` files, too. In
-general, you should only commit files changed by `cargo dev bless` for the
-specific lint you are creating/editing. Note that if the generated files are
-empty, they should be removed.
+general, you should only commit files changed by `cargo bless` for the
+specific lint you are creating/editing.
> _Note:_ you can run multiple test files by specifying a comma separated list:
> `TESTNAME=foo_functions,test2,test3`.
@@ -169,7 +166,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions
from the lint to the code of the test file and compare that to the contents of a
`.fixed` file.
-Use `cargo dev bless` to automatically generate the `.fixed` file after running
+Use `cargo bless` to automatically generate the `.fixed` file while running
the tests.
[rustfix]: https://github.com/rust-lang/rustfix
@@ -417,7 +414,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
Now we should also run the full test suite with `cargo test`. At this point
running `cargo test` should produce the expected output. Remember to run `cargo
-dev bless` to update the `.stderr` file.
+bless` to update the `.stderr` file.
`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
implementation is not violating any Clippy lints itself.
@@ -630,8 +627,14 @@ Before submitting your PR make sure you followed all the basic requirements:
## Adding configuration to a lint
-Clippy supports the configuration of lints values using a `clippy.toml` file in
-the workspace directory. Adding a configuration to a lint can be useful for
+Clippy supports the configuration of lints values using a `clippy.toml` file which is searched for in:
+
+1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or
+2. The directory specified by the
+[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or
+3. The current directory.
+
+Adding a configuration to a lint can be useful for
thresholds or to constrain some behavior that can be seen as a false positive
for some users. Adding a configuration is done in the following steps:
diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md
index 7615dc12f..f4c109ff1 100644
--- a/src/tools/clippy/book/src/development/basics.md
+++ b/src/tools/clippy/book/src/development/basics.md
@@ -66,7 +66,7 @@ If the output of a [UI test] differs from the expected output, you can update
the reference file with:
```bash
-cargo dev bless
+cargo bless
```
For example, this is necessary if you fix a typo in an error message of a lint,
diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
index df9b1bbe1..524454944 100644
--- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
+++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
@@ -56,6 +56,28 @@ and open that file in your editor of choice.
When updating the changelog it's also a good idea to make sure that `commit1` is
already correct in the current changelog.
+#### PR ranges
+
+We developed the concept of PR ranges to help the user understand the size of a new update. To create a PR range,
+get the current release date and the date that the last version was released (YYYY-MM-DD) and use the following link:
+
+```
+[**View <NUMBER OF PRs> PRs merged since 1.<LAST VERSION NUM>**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A<LAST VERSION DATE>..<CURRENT VERSION DATE>+base%3Amaster+sort%3Amerged-desc+)
+```
+
+> Note: Be sure to check click the link and check how many PRs got merged between
+
+Example:
+
+```
+[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
+```
+
+Which renders to:
+[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
+
+Note that **commit ranges should not be included**, only PR ranges.
+
### 3. Authoring the final changelog
The above script should have dumped all the relevant PRs to the file you
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 5646c9b15..60d7ce6e6 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -3,63 +3,14 @@ This file is generated by `cargo collect-metadata`.
Please use that command to update the file and do not edit it by hand.
-->
-## Lint Configuration Options
-| <div style="width:290px">Option</div> | Default Value |
-|--|--|
-| [arithmetic-side-effects-allowed](#arithmetic-side-effects-allowed) | `{}` |
-| [arithmetic-side-effects-allowed-binary](#arithmetic-side-effects-allowed-binary) | `[]` |
-| [arithmetic-side-effects-allowed-unary](#arithmetic-side-effects-allowed-unary) | `{}` |
-| [avoid-breaking-exported-api](#avoid-breaking-exported-api) | `true` |
-| [msrv](#msrv) | `None` |
-| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` |
-| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` |
-| [semicolon-inside-block-ignore-singleline](#semicolon-inside-block-ignore-singleline) | `false` |
-| [semicolon-outside-block-ignore-multiline](#semicolon-outside-block-ignore-multiline) | `false` |
-| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` |
-| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` |
-| [type-complexity-threshold](#type-complexity-threshold) | `250` |
-| [single-char-binding-names-threshold](#single-char-binding-names-threshold) | `4` |
-| [too-large-for-stack](#too-large-for-stack) | `200` |
-| [enum-variant-name-threshold](#enum-variant-name-threshold) | `3` |
-| [enum-variant-size-threshold](#enum-variant-size-threshold) | `200` |
-| [verbose-bit-mask-threshold](#verbose-bit-mask-threshold) | `1` |
-| [literal-representation-threshold](#literal-representation-threshold) | `16384` |
-| [trivial-copy-size-limit](#trivial-copy-size-limit) | `None` |
-| [pass-by-value-size-limit](#pass-by-value-size-limit) | `256` |
-| [too-many-lines-threshold](#too-many-lines-threshold) | `100` |
-| [array-size-threshold](#array-size-threshold) | `512000` |
-| [vec-box-size-threshold](#vec-box-size-threshold) | `4096` |
-| [max-trait-bounds](#max-trait-bounds) | `3` |
-| [max-struct-bools](#max-struct-bools) | `3` |
-| [max-fn-params-bools](#max-fn-params-bools) | `3` |
-| [warn-on-all-wildcard-imports](#warn-on-all-wildcard-imports) | `false` |
-| [disallowed-macros](#disallowed-macros) | `[]` |
-| [disallowed-methods](#disallowed-methods) | `[]` |
-| [disallowed-types](#disallowed-types) | `[]` |
-| [unreadable-literal-lint-fractions](#unreadable-literal-lint-fractions) | `true` |
-| [upper-case-acronyms-aggressive](#upper-case-acronyms-aggressive) | `false` |
-| [matches-for-let-else](#matches-for-let-else) | `WellKnownTypes` |
-| [cargo-ignore-publish](#cargo-ignore-publish) | `false` |
-| [standard-macro-braces](#standard-macro-braces) | `[]` |
-| [enforced-import-renames](#enforced-import-renames) | `[]` |
-| [allowed-scripts](#allowed-scripts) | `["Latin"]` |
-| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` |
-| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` |
-| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` |
-| [max-include-file-size](#max-include-file-size) | `1000000` |
-| [allow-expect-in-tests](#allow-expect-in-tests) | `false` |
-| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` |
-| [allow-dbg-in-tests](#allow-dbg-in-tests) | `false` |
-| [allow-print-in-tests](#allow-print-in-tests) | `false` |
-| [large-error-threshold](#large-error-threshold) | `128` |
-| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` |
-| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
-| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
-| [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` |
-| [future-size-threshold](#future-size-threshold) | `16384` |
-| [unnecessary-box-size](#unnecessary-box-size) | `128` |
-
-### arithmetic-side-effects-allowed
+# Lint Configuration Options
+
+The following list shows each configuration option, along with a description, its default value, an example
+and lints affected.
+
+---
+
+## `arithmetic-side-effects-allowed`
Suppress checking of the passed type names in all types of operations.
If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
@@ -77,10 +28,12 @@ A type, say `SomeType`, listed in this configuration has the same behavior of
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
-* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+---
+**Affected lints:**
+* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
-### arithmetic-side-effects-allowed-binary
+## `arithmetic-side-effects-allowed-binary`
Suppress checking of the passed type pair names in binary operations like addition or
multiplication.
@@ -98,10 +51,12 @@ arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType",
**Default Value:** `[]` (`Vec<[String; 2]>`)
-* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+---
+**Affected lints:**
+* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
-### arithmetic-side-effects-allowed-unary
+## `arithmetic-side-effects-allowed-unary`
Suppress checking of the passed type names in unary operations like "negation" (`-`).
#### Example
@@ -112,116 +67,145 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
-* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+---
+**Affected lints:**
+* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
-### avoid-breaking-exported-api
+## `avoid-breaking-exported-api`
Suppress lints whenever the suggested change would cause breakage for other crates.
**Default Value:** `true` (`bool`)
-* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
-* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
-* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
-* [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps)
-* [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self)
-* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
-* [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention)
-* [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection)
-* [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
-* [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
-* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
-* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
-* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
-* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
-* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
-
-
-### msrv
+---
+**Affected lints:**
+* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
+* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
+* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
+* [`unnecessary_wraps`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps)
+* [`unused_self`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self)
+* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
+* [`wrong_self_convention`](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention)
+* [`box_collection`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection)
+* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
+* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
+* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
+* [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
+* [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
+* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
+* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
+* [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn)
+
+
+## `msrv`
The minimum rust version that the project supports
**Default Value:** `None` (`Option<String>`)
-* [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
-* [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
-* [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
-* [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
-* [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
-* [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next)
-* [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
-* [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
-* [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
-* [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
-* [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
-* [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
-* [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
-* [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
-* [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
-* [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
-* [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
-* [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
-* [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
-* [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
-* [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
-* [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
-* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
-* [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
-* [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
-* [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
-* [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect)
-* [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
-* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
-* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
-* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
-* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
-* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
-* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
-* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
-* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
-* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
-* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
-* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
-* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
-* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
-* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
-* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
-
-
-### cognitive-complexity-threshold
+---
+**Affected lints:**
+* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
+* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
+* [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
+* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
+* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
+* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
+* [`filter_map_next`](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next)
+* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
+* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
+* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
+* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
+* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
+* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
+* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
+* [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
+* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
+* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
+* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
+* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
+* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
+* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
+* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
+* [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
+* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
+* [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
+* [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
+* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
+* [`err_expect`](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect)
+* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
+* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
+* [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
+* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
+* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
+* [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
+* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
+* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
+* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
+* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
+* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
+* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
+* [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
+* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
+* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
+* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
+* [`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)
+
+
+## `cognitive-complexity-threshold`
The maximum cognitive complexity a function can have
**Default Value:** `25` (`u64`)
-* [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
+---
+**Affected lints:**
+* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
+
+
+## `excessive-nesting-threshold`
+The maximum amount of nesting a block can reside in
+**Default Value:** `0` (`u64`)
-### disallowed-names
+---
+**Affected lints:**
+* [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting)
+
+
+## `disallowed-names`
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.
**Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
-* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
+---
+**Affected lints:**
+* [`disallowed_names`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
-### semicolon-inside-block-ignore-singleline
+## `semicolon-inside-block-ignore-singleline`
Whether to lint only if it's multiline.
**Default Value:** `false` (`bool`)
-* [semicolon_inside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)
+---
+**Affected lints:**
+* [`semicolon_inside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)
-### semicolon-outside-block-ignore-multiline
+## `semicolon-outside-block-ignore-multiline`
Whether to lint only if it's singleline.
**Default Value:** `false` (`bool`)
-* [semicolon_outside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)
+---
+**Affected lints:**
+* [`semicolon_outside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)
-### doc-valid-idents
+## `doc-valid-idents`
The list of words this lint should not consider as identifiers needing ticks. 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. For example:
@@ -230,207 +214,267 @@ default configuration of Clippy. By default, any configuration will replace the
Default list:
-**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "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"]` (`Vec<String>`)
-* [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown)
+---
+**Affected lints:**
+* [`doc_markdown`](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown)
-### too-many-arguments-threshold
+## `too-many-arguments-threshold`
The maximum number of argument a function or method can have
**Default Value:** `7` (`u64`)
-* [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)
+---
+**Affected lints:**
+* [`too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)
-### type-complexity-threshold
+## `type-complexity-threshold`
The maximum complexity a type can have
**Default Value:** `250` (`u64`)
-* [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity)
+---
+**Affected lints:**
+* [`type_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity)
-### single-char-binding-names-threshold
+## `single-char-binding-names-threshold`
The maximum number of single char bindings a scope may have
**Default Value:** `4` (`u64`)
-* [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
+---
+**Affected lints:**
+* [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
-### too-large-for-stack
+## `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`)
-* [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local)
-* [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
+---
+**Affected lints:**
+* [`boxed_local`](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local)
+* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
-### enum-variant-name-threshold
+## `enum-variant-name-threshold`
The minimum number of enum variants for the lints about variant names to trigger
**Default Value:** `3` (`u64`)
-* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
+---
+**Affected lints:**
+* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
-### enum-variant-size-threshold
+## `enum-variant-size-threshold`
The maximum size of an enum's variant to avoid box suggestion
**Default Value:** `200` (`u64`)
-* [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
+---
+**Affected lints:**
+* [`large_enum_variant`](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
-### verbose-bit-mask-threshold
+## `verbose-bit-mask-threshold`
The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
**Default Value:** `1` (`u64`)
-* [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask)
+---
+**Affected lints:**
+* [`verbose_bit_mask`](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask)
-### literal-representation-threshold
+## `literal-representation-threshold`
The lower bound for linting decimal literals
**Default Value:** `16384` (`u64`)
-* [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation)
+---
+**Affected lints:**
+* [`decimal_literal_representation`](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation)
-### trivial-copy-size-limit
+## `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>`)
-* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
+---
+**Affected lints:**
+* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
-### pass-by-value-size-limit
+## `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`)
-* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
+---
+**Affected lints:**
+* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
-### too-many-lines-threshold
+## `too-many-lines-threshold`
The maximum number of lines a function or method can have
**Default Value:** `100` (`u64`)
-* [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
+---
+**Affected lints:**
+* [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
-### array-size-threshold
+## `array-size-threshold`
The maximum allowed size for arrays on the stack
**Default Value:** `512000` (`u64`)
-* [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays)
-* [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays)
+---
+**Affected lints:**
+* [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays)
+* [`large_const_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays)
+
+
+## `stack-size-threshold`
+The maximum allowed stack size for functions in bytes
+
+**Default Value:** `512000` (`u64`)
+
+---
+**Affected lints:**
+* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames)
-### vec-box-size-threshold
+## `vec-box-size-threshold`
The size of the boxed type in bytes, where boxing in a `Vec` is allowed
**Default Value:** `4096` (`u64`)
-* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
+---
+**Affected lints:**
+* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
-### max-trait-bounds
+## `max-trait-bounds`
The maximum number of bounds a trait can have to be linted
**Default Value:** `3` (`u64`)
-* [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
+---
+**Affected lints:**
+* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
-### max-struct-bools
+## `max-struct-bools`
The maximum number of bool fields a struct can have
**Default Value:** `3` (`u64`)
-* [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools)
+---
+**Affected lints:**
+* [`struct_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools)
-### max-fn-params-bools
+## `max-fn-params-bools`
The maximum number of bool parameters a function can have
**Default Value:** `3` (`u64`)
-* [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools)
+---
+**Affected lints:**
+* [`fn_params_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools)
-### warn-on-all-wildcard-imports
+## `warn-on-all-wildcard-imports`
Whether to allow certain wildcard imports (prelude, super in tests).
**Default Value:** `false` (`bool`)
-* [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
+---
+**Affected lints:**
+* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
-### disallowed-macros
+## `disallowed-macros`
The list of disallowed macros, written as fully qualified paths.
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
-* [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros)
+---
+**Affected lints:**
+* [`disallowed_macros`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros)
-### disallowed-methods
+## `disallowed-methods`
The list of disallowed methods, written as fully qualified paths.
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
-* [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
+---
+**Affected lints:**
+* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
-### disallowed-types
+## `disallowed-types`
The list of disallowed types, written as fully qualified paths.
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
-* [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)
+---
+**Affected lints:**
+* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)
-### unreadable-literal-lint-fractions
+## `unreadable-literal-lint-fractions`
Should the fraction of a decimal be linted to include separators.
**Default Value:** `true` (`bool`)
-* [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal)
+---
+**Affected lints:**
+* [`unreadable_literal`](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal)
-### upper-case-acronyms-aggressive
+## `upper-case-acronyms-aggressive`
Enables verbose mode. Triggers if there is more than one uppercase char next to each other
**Default Value:** `false` (`bool`)
-* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
+---
+**Affected lints:**
+* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
-### matches-for-let-else
+## `matches-for-let-else`
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`)
-* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
+---
+**Affected lints:**
+* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
-### cargo-ignore-publish
+## `cargo-ignore-publish`
For internal testing only, ignores the current `publish` settings in the Cargo manifest.
**Default Value:** `false` (`bool`)
-* [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
+---
+**Affected lints:**
+* [`_cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
-### standard-macro-braces
+## `standard-macro-braces`
Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
@@ -439,119 +483,147 @@ could be used with a full path two `MacroMatcher`s have to be added one with the
**Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
-* [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces)
+---
+**Affected lints:**
+* [`nonstandard_macro_braces`](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces)
-### enforced-import-renames
+## `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>`)
-* [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames)
+---
+**Affected lints:**
+* [`missing_enforced_import_renames`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames)
-### allowed-scripts
+## `allowed-scripts`
The list of unicode scripts allowed to be used in the scope.
**Default Value:** `["Latin"]` (`Vec<String>`)
-* [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents)
+---
+**Affected lints:**
+* [`disallowed_script_idents`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents)
-### enable-raw-pointer-heuristic-for-send
+## `enable-raw-pointer-heuristic-for-send`
Whether to apply the raw pointer heuristic to determine if a type is `Send`.
**Default Value:** `true` (`bool`)
-* [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty)
+---
+**Affected lints:**
+* [`non_send_fields_in_send_ty`](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty)
-### max-suggested-slice-pattern-length
+## `max-suggested-slice-pattern-length`
When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
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`)
-* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
+---
+**Affected lints:**
+* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
-### await-holding-invalid-types
+## `await-holding-invalid-types`
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
-* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
+---
+**Affected lints:**
+* [`await_holding_invalid_type`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
-### max-include-file-size
+## `max-include-file-size`
The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
**Default Value:** `1000000` (`u64`)
-* [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file)
+---
+**Affected lints:**
+* [`large_include_file`](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file)
-### allow-expect-in-tests
+## `allow-expect-in-tests`
Whether `expect` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
-* [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
+---
+**Affected lints:**
+* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
-### allow-unwrap-in-tests
+## `allow-unwrap-in-tests`
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
-* [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
+---
+**Affected lints:**
+* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
-### allow-dbg-in-tests
+## `allow-dbg-in-tests`
Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
-* [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
+---
+**Affected lints:**
+* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
-### allow-print-in-tests
+## `allow-print-in-tests`
Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
-* [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout)
-* [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr)
+---
+**Affected lints:**
+* [`print_stdout`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout)
+* [`print_stderr`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr)
-### large-error-threshold
+## `large-error-threshold`
The maximum size of the `Err`-variant in a `Result` returned from a function
**Default Value:** `128` (`u64`)
-* [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
+---
+**Affected lints:**
+* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
-### ignore-interior-mutability
+## `ignore-interior-mutability`
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>`)
-* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
-* [ifs_same_cond](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
+---
+**Affected lints:**
+* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
+* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
-### allow-mixed-uninlined-format-args
+## `allow-mixed-uninlined-format-args`
Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
**Default Value:** `true` (`bool`)
-* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
+---
+**Affected lints:**
+* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
-### suppress-restriction-lint-in-const
+## `suppress-restriction-lint-in-const`
Whether to suppress a restriction lint in constant code. In same
cases the restructured operation might not be unavoidable, as the
suggested counterparts are unavailable in constant code. This
@@ -560,32 +632,101 @@ if no suggestion can be made.
**Default Value:** `false` (`bool`)
-* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
+---
+**Affected lints:**
+* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
-### missing-docs-in-crate-items
+## `missing-docs-in-crate-items`
Whether to **only** check for missing documentation in items visible within the current
crate. For example, `pub(crate)` items.
**Default Value:** `false` (`bool`)
-* [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
+---
+**Affected lints:**
+* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
-### future-size-threshold
+## `future-size-threshold`
The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
**Default Value:** `16384` (`u64`)
-* [large_futures](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
+---
+**Affected lints:**
+* [`large_futures`](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
-### unnecessary-box-size
+## `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`)
-* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
+---
+**Affected lints:**
+* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
+
+
+## `allow-private-module-inception`
+Whether to allow module inception if it's not public.
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
+
+
+## `allowed-idents-below-min-chars`
+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.
+
+**Default Value:** `{"j", "z", "i", "y", "n", "x", "w"}` (`rustc_data_structures::fx::FxHashSet<String>`)
+
+---
+**Affected lints:**
+* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
+
+
+## `min-ident-chars-threshold`
+Minimum chars an ident can have, anything below or equal to this will be linted.
+
+**Default Value:** `1` (`u64`)
+
+---
+**Affected lints:**
+* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
+
+
+## `accept-comment-above-statement`
+Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks)
+
+
+## `accept-comment-above-attributes`
+Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks)
+
+
+## `allow-one-hash-in-raw-strings`
+Whether to allow `r#""#` when `r""` can be used
+
+**Default Value:** `false` (`bool`)
+---
+**Affected lints:**
+* [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes)
diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs
deleted file mode 100644
index 92b2771f3..000000000
--- a/src/tools/clippy/clippy_dev/src/bless.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-//! `bless` updates the reference files in the repo with changed output files
-//! from the last test run.
-
-use crate::cargo_clippy_path;
-use std::ffi::OsStr;
-use std::fs;
-use std::path::{Path, PathBuf};
-use std::sync::LazyLock;
-use walkdir::{DirEntry, WalkDir};
-
-static CLIPPY_BUILD_TIME: LazyLock<Option<std::time::SystemTime>> =
- LazyLock::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
-
-/// # Panics
-///
-/// Panics if the path to a test file is broken
-pub fn bless(ignore_timestamp: bool) {
- let extensions = ["stdout", "stderr", "fixed"].map(OsStr::new);
-
- WalkDir::new(build_dir())
- .into_iter()
- .map(Result::unwrap)
- .filter(|entry| entry.path().extension().map_or(false, |ext| extensions.contains(&ext)))
- .for_each(|entry| update_reference_file(&entry, ignore_timestamp));
-}
-
-fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
- let test_output_path = test_output_entry.path();
-
- let reference_file_name = test_output_entry.file_name().to_str().unwrap().replace(".stage-id", "");
- let reference_file_path = Path::new("tests")
- .join(test_output_path.strip_prefix(build_dir()).unwrap())
- .with_file_name(reference_file_name);
-
- // If the test output was not updated since the last clippy build, it may be outdated
- if !ignore_timestamp && !updated_since_clippy_build(test_output_entry).unwrap_or(true) {
- return;
- }
-
- let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
- let reference_file = fs::read(&reference_file_path).unwrap_or_default();
-
- if test_output_file != reference_file {
- // If a test run caused an output file to change, update the reference file
- println!("updating {}", reference_file_path.display());
- fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
- }
-}
-
-fn updated_since_clippy_build(entry: &DirEntry) -> Option<bool> {
- let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
- let modified = entry.metadata().ok()?.modified().ok()?;
- Some(modified >= clippy_build_time)
-}
-
-fn build_dir() -> PathBuf {
- let mut path = std::env::current_exe().unwrap();
- path.set_file_name("test");
- path
-}
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index 256231441..ee559d45d 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -35,6 +35,7 @@ struct FmtContext {
}
// the "main" function of cargo dev fmt
+#[allow(clippy::missing_panics_doc)]
pub fn run(check: bool, verbose: bool) {
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
let mut success = true;
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 56a269288..4624451cf 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -14,7 +14,6 @@ use std::io;
use std::path::PathBuf;
use std::process::{self, ExitStatus};
-pub mod bless;
pub mod dogfood;
pub mod fmt;
pub mod lint;
@@ -29,6 +28,10 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
/// Returns the path to the `cargo-clippy` binary
+///
+/// # Panics
+///
+/// Panics if the path of current executable could not be retrieved.
#[must_use]
pub fn cargo_clippy_path() -> PathBuf {
let mut path = std::env::current_exe().expect("failed to get current executable name");
@@ -61,6 +64,8 @@ pub fn clippy_project_root() -> PathBuf {
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
}
+/// # Panics
+/// Panics if given command result was failed.
pub fn exit_if_err(status: io::Result<ExitStatus>) {
match status.expect("failed to run command").code() {
Some(0) => {},
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index e2457e5a8..43eaccdf5 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -3,15 +3,16 @@
#![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{Arg, ArgAction, ArgMatches, Command};
-use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
+use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
use indoc::indoc;
+use std::convert::Infallible;
fn main() {
let matches = get_clap_config();
match matches.subcommand() {
- Some(("bless", matches)) => {
- bless::bless(matches.get_flag("ignore-timestamp"));
+ Some(("bless", _)) => {
+ eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
},
Some(("dogfood", matches)) => {
dogfood::dogfood(
@@ -34,7 +35,7 @@ fn main() {
},
Some(("new_lint", matches)) => {
match new_lint::create(
- matches.get_one::<String>("pass"),
+ matches.get_one::<String>("pass").unwrap(),
matches.get_one::<String>("name"),
matches.get_one::<String>("category").map(String::as_str),
matches.get_one::<String>("type").map(String::as_str),
@@ -175,12 +176,13 @@ fn get_clap_config() -> ArgMatches {
.help("Specify whether the lint runs during the early or late pass")
.value_parser(["early", "late"])
.conflicts_with("type")
- .required_unless_present("type"),
+ .default_value("late"),
Arg::new("name")
.short('n')
.long("name")
.help("Name of the new lint in snake case, ex: fn_too_long")
- .required(true),
+ .required(true)
+ .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))),
Arg::new("category")
.short('c')
.long("category")
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 13a277034..f11aa547b 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -36,8 +36,9 @@ impl<T> Context for io::Result<T> {
/// # Errors
///
/// This function errors out if the files couldn't be created or written to.
+#[allow(clippy::missing_panics_doc)]
pub fn create(
- pass: Option<&String>,
+ pass: &String,
lint_name: Option<&String>,
category: Option<&str>,
mut ty: Option<&str>,
@@ -49,7 +50,7 @@ pub fn create(
}
let lint = LintData {
- pass: pass.map_or("", String::as_str),
+ pass,
name: lint_name.expect("`name` argument is validated by clap"),
category: category.expect("`category` argument is validated by clap"),
ty,
@@ -63,6 +64,14 @@ pub fn create(
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
}
+ if pass == "early" {
+ println!(
+ "\n\
+ NOTE: Use a late pass unless you need something specific from\
+ an early pass, as they lack many features and utilities"
+ );
+ }
+
Ok(())
}
@@ -87,7 +96,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
path.push("src");
fs::create_dir(&path)?;
- let header = format!("// compile-flags: --crate-name={lint_name}");
+ let header = format!("//@compile-flags: --crate-name={lint_name}");
write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
Ok(())
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 98e69c7fd..c23054443 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.71"
+version = "0.1.72"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -20,20 +20,19 @@ quine-mc_cluskey = "0.2"
regex-syntax = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
-tempfile = { version = "3.2", optional = true }
-toml = "0.5"
+tempfile = { version = "3.3.0", optional = true }
+toml = "0.7.3"
+regex = { version = "1.5", optional = true }
unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false }
semver = "1.0"
rustc-semver = "1.1"
-# NOTE: cargo requires serde feat in its url dep
-# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
-url = { version = "2.2", features = ["serde"] }
+url = "2.2"
[features]
deny-warnings = ["clippy_utils/deny-warnings"]
# build clippy with internal lints enabled, off by default
-internal = ["clippy_utils/internal", "serde_json", "tempfile"]
+internal = ["clippy_utils/internal", "serde_json", "tempfile", "regex"]
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
index add73d0ae..eb2118471 100644
--- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
@@ -1,5 +1,5 @@
-use ast::AttrStyle;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use ast::{AttrStyle, Attribute};
+use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro};
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -40,7 +40,7 @@ declare_clippy_lint! {
/// a.len()
/// }
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub ALLOW_ATTRIBUTES,
restriction,
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
@@ -50,13 +50,14 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
impl LateLintPass<'_> for AllowAttribute {
// Separate each crate's features.
- fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+ fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
if_chain! {
if !in_external_macro(cx.sess(), attr.span);
if cx.tcx.features().lint_reasons;
if let AttrStyle::Outer = attr.style;
if let Some(ident) = attr.ident();
if ident.name == rustc_span::symbol::sym::allow;
+ if !is_from_proc_macro(cx, &attr);
then {
span_lint_and_sugg(
cx,
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
new file mode 100644
index 000000000..98ee8a9a8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
@@ -0,0 +1,79 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::last_path_segment;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_lint::LateLintPass;
+use rustc_middle::ty;
+use rustc_middle::ty::print::with_forced_trimmed_paths;
+use rustc_middle::ty::GenericArgKind;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+ /// ### What it does.
+ /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`.
+ ///
+ /// ### Why is this bad?
+ /// `Arc<T>` is only `Send`/`Sync` when `T` is [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E),
+ /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::cell::RefCell;
+ /// # use std::sync::Arc;
+ ///
+ /// fn main() {
+ /// // This is fine, as `i32` implements `Send` and `Sync`.
+ /// let a = Arc::new(42);
+ ///
+ /// // `RefCell` is `!Sync`, so either the `Arc` should be replaced with an `Rc`
+ /// // or the `RefCell` replaced with something like a `RwLock`
+ /// let b = Arc::new(RefCell::new(42));
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub ARC_WITH_NON_SEND_SYNC,
+ suspicious,
+ "using `Arc` with a type that does not implement `Send` or `Sync`"
+}
+declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
+
+impl LateLintPass<'_> for ArcWithNonSendSync {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ let ty = cx.typeck_results().expr_ty(expr);
+ if is_type_diagnostic_item(cx, ty, sym::Arc)
+ && let ExprKind::Call(func, [arg]) = expr.kind
+ && let ExprKind::Path(func_path) = func.kind
+ && last_path_segment(&func_path).ident.name == sym::new
+ && let arg_ty = cx.typeck_results().expr_ty(arg)
+ // make sure that the type is not and does not contain any type parameters
+ && arg_ty.walk().all(|arg| {
+ !matches!(arg.unpack(), GenericArgKind::Type(ty) if matches!(ty.kind(), ty::Param(_)))
+ })
+ && let Some(send) = cx.tcx.get_diagnostic_item(sym::Send)
+ && let Some(sync) = cx.tcx.lang_items().sync_trait()
+ && let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
+ && !(is_send && is_sync)
+ {
+ span_lint_and_then(
+ cx,
+ 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.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`");
+ }
+ ));
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
index c7a76e5f9..b9dda49ca 100644
--- a/src/tools/clippy/clippy_lints/src/as_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_ast::ast::{Expr, ExprKind};
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use clippy_utils::is_from_proc_macro;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -45,9 +46,9 @@ declare_clippy_lint! {
declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
-impl EarlyLintPass for AsConversions {
- fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if in_external_macro(cx.sess(), expr.span) {
+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;
}
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 897495ba1..2ba78f995 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -1,9 +1,12 @@
//! checks for attributes
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
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 clippy_utils::{
+ diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then},
+ is_from_proc_macro,
+};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_errors::Applicability;
@@ -362,6 +365,32 @@ declare_clippy_lint! {
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `#[cfg(features = "...")]` and suggests to replace it with
+ /// `#[cfg(feature = "...")]`.
+ ///
+ /// ### Why is this bad?
+ /// Misspelling `feature` as `features` can be sometimes hard to spot. It
+ /// may cause conditional compilation not work quitely.
+ ///
+ /// ### Example
+ /// ```rust
+ /// #[cfg(features = "some-feature")]
+ /// fn conditional() { }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// #[cfg(feature = "some-feature")]
+ /// fn conditional() { }
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub MAYBE_MISUSED_CFG,
+ suspicious,
+ "prevent from misusing the wrong attr name"
+}
+
declare_lint_pass!(Attributes => [
ALLOW_ATTRIBUTES_WITHOUT_REASON,
INLINE_ALWAYS,
@@ -540,7 +569,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
}
}
-fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
+fn check_lint_reason<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
// Check for the feature
if !cx.tcx.features().lint_reasons {
return;
@@ -555,7 +584,7 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
}
// Check if the attribute is in an external macro and therefore out of the developer's control
- if in_external_macro(cx.sess(), attr.span) {
+ if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) {
return;
}
@@ -676,6 +705,7 @@ impl_lint_pass!(EarlyAttributes => [
EMPTY_LINE_AFTER_OUTER_ATTR,
EMPTY_LINE_AFTER_DOC_COMMENTS,
NON_MINIMAL_CFG,
+ MAYBE_MISUSED_CFG,
]);
impl EarlyLintPass for EarlyAttributes {
@@ -687,6 +717,7 @@ impl EarlyLintPass for EarlyAttributes {
check_deprecated_cfg_attr(cx, attr, &self.msrv);
check_mismatched_target_os(cx, attr);
check_minimal_cfg_condition(cx, attr);
+ check_misused_cfg(cx, attr);
}
extract_msrv_attr!(EarlyContext);
@@ -777,7 +808,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
}
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
- for item in items.iter() {
+ for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
continue;
@@ -810,6 +841,27 @@ 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() {
+ span_lint_and_sugg(
+ cx,
+ MAYBE_MISUSED_CFG,
+ meta.span,
+ "feature may misspelled as features",
+ "use",
+ format!("feature = \"{val}\""),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ if let MetaItemKind::List(list) = &meta.kind {
+ check_nested_misused_cfg(cx, list);
+ }
+ }
+ }
+}
+
fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg) &&
let Some(items) = attr.meta_item_list()
@@ -818,6 +870,14 @@ fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
}
}
+fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
+ if attr.has_name(sym::cfg) &&
+ let Some(items) = attr.meta_item_list()
+ {
+ check_nested_misused_cfg(cx, &items);
+ }
+}
+
fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
fn find_os(name: &str) -> Option<&'static str> {
UNIX_SYSTEMS
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 8c3ad24ee..e8775b081 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -61,7 +61,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
)
})
.map_or(false, |assoc_item| {
- let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, []));
+ let proj = Ty::new_projection(cx.tcx,assoc_item.def_id, cx.tcx.mk_substs_trait(ty, []));
let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
nty.is_bool()
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 455f0df7c..04cca9e31 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -88,7 +88,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
NonminimalBoolVisitor { cx }.visit_body(body);
}
}
-
struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
@@ -441,7 +440,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
diag.span_suggestions(
e.span,
"try",
- suggestions.into_iter(),
+ suggestions,
// nonminimal_bool can produce minimal but
// not human readable expressions (#3141)
Applicability::Unspecified,
@@ -473,6 +472,10 @@ 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() {
+ return;
+ }
if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
self.bool_expr(e);
}
diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
index 294d22d34..b7256dd2e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
@@ -4,6 +4,7 @@ use clippy_utils::source::snippet_with_context;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::Adjust;
use super::BORROW_AS_PTR;
@@ -23,6 +24,15 @@ pub(super) fn check<'tcx>(
};
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
+ // Fix #9884
+ if !e.is_place_expr(|base| {
+ cx.typeck_results()
+ .adjustments()
+ .get(base.hir_id)
+ .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
+ }) {
+ return;
+ }
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
index 28ecdea7e..ffa571abb 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
@@ -1,41 +1,90 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::is_isize_or_usize;
use rustc_hir::Expr;
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::Ty;
use super::{utils, CAST_POSSIBLE_WRAP};
+// this should be kept in sync with the allowed bit widths of `usize` and `isize`
+const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64];
+
+// whether the lint should be emitted, and the required pointer size, if it matters
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum EmitState {
+ NoLint,
+ LintAlways,
+ LintOnPtrSize(u64),
+}
+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
if !(cast_from.is_integral() && cast_to.is_integral()) {
return;
}
- let arch_64_suffix = " on targets with 64-bit wide pointers";
- let arch_32_suffix = " on targets with 32-bit wide pointers";
- let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
+ // emit a lint if a cast is:
+ // 1. unsigned to signed
+ // and
+ // 2. either:
+ //
+ // 2a. between two types of constant size that are always the same size
+ // 2b. between one target-dependent size and one constant size integer,
+ // and the constant integer is in the allowed set of target dependent sizes
+ // (the ptr size could be chosen to be the same as the constant size)
+
+ if cast_from.is_signed() || !cast_to.is_signed() {
+ return;
+ }
+
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
- let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
- (true, true) | (false, false) => (to_nbits == from_nbits && cast_unsigned_to_signed, ""),
- (true, false) => (to_nbits <= 32 && cast_unsigned_to_signed, arch_32_suffix),
- (false, true) => (
- cast_unsigned_to_signed,
- if from_nbits == 64 {
- arch_64_suffix
+ let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) {
+ (true, true) => {
+ // casts between two ptr sized integers are trivially always the same size
+ // so do not depend on any specific pointer size to be the same
+ EmitState::LintAlways
+ },
+ (true, false) => {
+ // the first type is `usize` and the second is a constant sized signed integer
+ if ALLOWED_POINTER_SIZES.contains(&to_nbits) {
+ EmitState::LintOnPtrSize(to_nbits)
+ } else {
+ EmitState::NoLint
+ }
+ },
+ (false, true) => {
+ // the first type is a constant sized unsigned integer, and the second is `isize`
+ if ALLOWED_POINTER_SIZES.contains(&from_nbits) {
+ EmitState::LintOnPtrSize(from_nbits)
+ } else {
+ EmitState::NoLint
+ }
+ },
+ (false, false) => {
+ // the types are both a constant known size
+ // and do not depend on any specific pointer size to be the same
+ if from_nbits == to_nbits {
+ EmitState::LintAlways
} else {
- arch_32_suffix
- },
+ EmitState::NoLint
+ }
+ },
+ };
+
+ let message = match should_lint {
+ EmitState::NoLint => return,
+ EmitState::LintAlways => format!("casting `{cast_from}` to `{cast_to}` may wrap around the value"),
+ EmitState::LintOnPtrSize(ptr_size) => format!(
+ "casting `{cast_from}` to `{cast_to}` may wrap around the value on targets with {ptr_size}-bit wide pointers",
),
};
- if should_lint {
- span_lint(
- cx,
- CAST_POSSIBLE_WRAP,
- expr.span,
- &format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",),
- );
- }
+ cx.struct_span_lint(CAST_POSSIBLE_WRAP, expr.span, message, |diag| {
+ if let EmitState::LintOnPtrSize(16) = should_lint {
+ diag
+ .note("`usize` and `isize` may be as small as 16 bits on some platforms")
+ .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types")
+ } else {
+ diag
+ }
+ });
}
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs
deleted file mode 100644
index 15f2f81f4..000000000
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp};
-use rustc_lint::LateContext;
-use rustc_middle::ty;
-
-use super::CAST_REF_TO_MUT;
-
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
- if_chain! {
- if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind;
- if let ExprKind::Cast(e, t) = &e.kind;
- if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind;
- if let ExprKind::Cast(e, t) = &e.kind;
- if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind;
- if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind();
- then {
- span_lint(
- cx,
- CAST_REF_TO_MUT,
- expr.span,
- "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`",
- );
- }
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index cfeb75eed..0ac6ef649 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -9,7 +9,6 @@ mod cast_possible_truncation;
mod cast_possible_wrap;
mod cast_precision_loss;
mod cast_ptr_alignment;
-mod cast_ref_to_mut;
mod cast_sign_loss;
mod cast_slice_different_sizes;
mod cast_slice_from_raw_parts;
@@ -18,6 +17,7 @@ mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any;
mod fn_to_numeric_cast_with_truncation;
mod ptr_as_ptr;
+mod ptr_cast_constness;
mod unnecessary_cast;
mod utils;
@@ -118,9 +118,10 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for casts from an unsigned type to a signed type of
- /// the same size. Performing such a cast is a 'no-op' for the compiler,
- /// i.e., nothing is changed at the bit level, and the binary representation of
- /// the value is reinterpreted. This can cause wrapping if the value is too big
+ /// the same size, or possibly smaller due to target dependent integers.
+ /// Performing such a cast is a 'no-op' for the compiler, i.e., nothing is
+ /// changed at the bit level, and the binary representation of the value is
+ /// reinterpreted. This can cause wrapping if the value is too big
/// for the target signed type. However, the cast works as defined, so this lint
/// is `Allow` by default.
///
@@ -174,8 +175,8 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for casts to the same type, casts of int literals to integer types
- /// and casts of float literals to float types.
+ /// Checks for casts to the same type, casts of int literals to integer types, casts of float
+ /// literals to float types and casts between raw pointers without changing type or constness.
///
/// ### Why is this bad?
/// It's just unnecessary.
@@ -332,41 +333,6 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for casts of `&T` to `&mut T` anywhere in the code.
- ///
- /// ### Why is this bad?
- /// It’s basically guaranteed to be undefined behavior.
- /// `UnsafeCell` is the only way to obtain aliasable data that is considered
- /// mutable.
- ///
- /// ### Example
- /// ```rust,ignore
- /// fn x(r: &i32) {
- /// unsafe {
- /// *(r as *const _ as *mut _) += 1;
- /// }
- /// }
- /// ```
- ///
- /// Instead consider using interior mutability types.
- ///
- /// ```rust
- /// use std::cell::UnsafeCell;
- ///
- /// fn x(r: &UnsafeCell<i32>) {
- /// unsafe {
- /// *r.get() += 1;
- /// }
- /// }
- /// ```
- #[clippy::version = "1.33.0"]
- pub CAST_REF_TO_MUT,
- correctness,
- "a cast of reference to a mutable pointer"
-}
-
-declare_clippy_lint! {
- /// ### What it does
/// Checks for expressions where a character literal is cast
/// to `u8` and suggests using a byte literal instead.
///
@@ -399,7 +365,7 @@ declare_clippy_lint! {
/// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
///
/// ### Why is this bad?
- /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
+ /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because
/// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
///
/// ### Example
@@ -424,6 +390,34 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
+ /// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to
+ /// `*mut T` and `*mut T` to `*const T`.
+ ///
+ /// ### Why is this bad?
+ /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
+ /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
+ /// type.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let ptr: *const u32 = &42_u32;
+ /// let mut_ptr = ptr as *mut u32;
+ /// let ptr = mut_ptr as *const u32;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let ptr: *const u32 = &42_u32;
+ /// let mut_ptr = ptr.cast_mut();
+ /// let ptr = mut_ptr.cast_const();
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub PTR_CAST_CONSTNESS,
+ pedantic,
+ "casting using `as` from and to raw pointers to change constness when specialized methods apply"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Checks for casts from an enum type to an integral type which will definitely truncate the
/// value.
///
@@ -529,7 +523,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Check for the usage of `as _` conversion using inferred type.
+ /// Checks for the usage of `as _` conversion using inferred type.
///
/// ### Why is this bad?
/// The conversion might include lossy conversion and dangerous cast that might go
@@ -680,7 +674,6 @@ impl_lint_pass!(Casts => [
CAST_POSSIBLE_TRUNCATION,
CAST_POSSIBLE_WRAP,
CAST_LOSSLESS,
- CAST_REF_TO_MUT,
CAST_PTR_ALIGNMENT,
CAST_SLICE_DIFFERENT_SIZES,
UNNECESSARY_CAST,
@@ -689,6 +682,7 @@ impl_lint_pass!(Casts => [
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
CHAR_LIT_AS_U8,
PTR_AS_PTR,
+ PTR_CAST_CONSTNESS,
CAST_ENUM_TRUNCATION,
CAST_ENUM_CONSTRUCTOR,
CAST_ABS_TO_UNSIGNED,
@@ -722,6 +716,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
return;
}
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
+ ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
@@ -747,7 +742,6 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
}
}
- cast_ref_to_mut::check(cx, expr);
cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr);
ptr_as_ptr::check(cx, expr, &self.msrv);
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
new file mode 100644
index 000000000..f0c1df014
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -0,0 +1,45 @@
+use clippy_utils::msrvs::POINTER_CAST_CONSTNESS;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Mutability};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty, TypeAndMut};
+
+use super::PTR_CAST_CONSTNESS;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_expr: &Expr<'_>,
+ cast_from: Ty<'tcx>,
+ cast_to: Ty<'tcx>,
+ msrv: &Msrv,
+) {
+ if_chain! {
+ if msrv.meets(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),
+ (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not));
+ if from_ty == to_ty;
+ then {
+ let sugg = Sugg::hir(cx, cast_expr, "_");
+ let constness = match *to_mutbl {
+ Mutability::Not => "const",
+ Mutability::Mut => "mut",
+ };
+
+ span_lint_and_sugg(
+ cx,
+ PTR_CAST_CONSTNESS,
+ expr.span,
+ "`as` casting between raw pointers while changing only its constness",
+ &format!("try `pointer::cast_{constness}`, a safer alternative"),
+ format!("{}.cast_{constness}()", sugg.maybe_par()),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
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 804ae8411..71cf2aea0 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,18 +1,21 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{get_parent_expr, path_to_local};
+use clippy_utils::visitors::{for_each_expr, Visitable};
+use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use if_chain::if_chain;
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
-use rustc_hir::def::Res;
-use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
+use std::ops::ControlFlow;
use super::UNNECESSARY_CAST;
+#[expect(clippy::too_many_lines)]
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &Expr<'tcx>,
@@ -20,19 +23,84 @@ pub(super) fn check<'tcx>(
cast_from: Ty<'tcx>,
cast_to: Ty<'tcx>,
) -> bool {
- // skip non-primitive type cast
+ let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
+
+ if_chain! {
+ if let ty::RawPtr(..) = cast_from.kind();
+ // check both mutability and type are the same
+ if cast_from.kind() == cast_to.kind();
+ if let ExprKind::Cast(_, cast_to_hir) = expr.kind;
+ // Ignore casts to e.g. type aliases and infer types
+ // - p as pointer_alias
+ // - p as _
+ if let TyKind::Ptr(to_pointee) = cast_to_hir.kind;
+ then {
+ match to_pointee.ty.kind {
+ // Ignore casts to pointers that are aliases or cfg dependant, e.g.
+ // - p as *const std::ffi::c_char (alias)
+ // - p as *const std::os::raw::c_char (cfg dependant)
+ TyKind::Path(qpath) => {
+ if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) {
+ return false;
+ }
+ },
+ // Ignore `p as *const _`
+ TyKind::Infer => return false,
+ _ => {},
+ }
+
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_CAST,
+ expr.span,
+ &format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"),
+ "try",
+ cast_str.clone(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ // skip cast of local that is a type alias
+ if let ExprKind::Cast(inner, ..) = expr.kind
+ && let ExprKind::Path(qpath) = inner.kind
+ && let QPath::Resolved(None, Path { res, .. }) = qpath
+ && let Res::Local(hir_id) = res
+ && let parent = cx.tcx.hir().get_parent(*hir_id)
+ && let Node::Local(local) = parent
+ {
+ if let Some(ty) = local.ty
+ && let TyKind::Path(qpath) = ty.kind
+ && is_ty_alias(&qpath)
+ {
+ return false;
+ }
+
+ if let Some(expr) = local.init
+ && let ExprKind::Cast(.., cast_to) = expr.kind
+ && let TyKind::Path(qpath) = cast_to.kind
+ && is_ty_alias(&qpath)
+ {
+ return false;
+ }
+ }
+
+ // 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) {
+ return false;
+ }
+
+ // skip cast to non-primitive type
if_chain! {
if let ExprKind::Cast(_, cast_to) = expr.kind;
if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
if let Res::PrimTy(_) = path.res;
then {}
else {
- return false
+ return false;
}
}
- let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
-
if let Some(lit) = get_numeric_literal(cast_expr) {
let literal_str = &cast_str;
@@ -162,3 +230,61 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
_ => 0,
}
}
+
+/// Finds whether an `Expr` returns a type alias.
+///
+/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
+/// dark path reimplementing this (or something similar).
+fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
+ for_each_expr(expr, |expr| {
+ // Calls are a `Path`, and usage of locals are a `Path`. So, this checks
+ // - call() as i32
+ // - local as i32
+ if let ExprKind::Path(qpath) = expr.kind {
+ let res = cx.qpath_res(&qpath, expr.hir_id);
+ // Function call
+ if let Res::Def(DefKind::Fn, def_id) = res {
+ let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
+ return ControlFlow::Continue(());
+ };
+ // This is the worst part of this entire function. This is the only way I know of to
+ // check whether a function returns a type alias. Sure, you can get the return type
+ // from a function in the current crate as an hir ty, but how do you get it for
+ // external functions?? Simple: It's impossible. So, we check whether a part of the
+ // function's declaration snippet is exactly equal to the `Ty`. That way, we can
+ // see whether it's a type alias.
+ //
+ // Will this work for more complex types? Probably not!
+ if !snippet
+ .split("->")
+ .skip(0)
+ .map(|s| {
+ s.trim() == cast_from.to_string()
+ || s.split("where").any(|ty| ty.trim() == cast_from.to_string())
+ })
+ .any(|a| a)
+ {
+ return ControlFlow::Break(());
+ }
+ // Local usage
+ } else if let Res::Local(hir_id) = res
+ && 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) {
+ return ControlFlow::Break::<()>(());
+ }
+
+ if let Some(ty) = l.ty
+ && let TyKind::Path(qpath) = ty.kind
+ && is_ty_alias(&qpath)
+ {
+ return ControlFlow::Break::<()>(());
+ }
+ }
+ }
+
+ ControlFlow::Continue(())
+ })
+ .is_some()
+}
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 5e2eb5789..ac5ac542c 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
@@ -37,7 +37,7 @@ declare_clippy_lint! {
/// println!("{sample}");
/// }
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub COLLECTION_IS_NEVER_READ,
nursery,
"a collection is never queried"
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 423eee477..9d9ee6ba3 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -4,6 +4,8 @@
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,
@@ -38,6 +40,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
+ crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO,
crate::as_conversions::AS_CONVERSIONS_INFO,
crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO,
@@ -51,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
crate::attrs::INLINE_ALWAYS_INFO,
+ crate::attrs::MAYBE_MISUSED_CFG_INFO,
crate::attrs::MISMATCHED_TARGET_OS_INFO,
crate::attrs::NON_MINIMAL_CFG_INFO,
crate::attrs::USELESS_ATTRIBUTE_INFO,
@@ -81,7 +85,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::casts::CAST_POSSIBLE_WRAP_INFO,
crate::casts::CAST_PRECISION_LOSS_INFO,
crate::casts::CAST_PTR_ALIGNMENT_INFO,
- crate::casts::CAST_REF_TO_MUT_INFO,
crate::casts::CAST_SIGN_LOSS_INFO,
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
@@ -90,6 +93,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
crate::casts::PTR_AS_PTR_INFO,
+ crate::casts::PTR_CAST_CONSTNESS_INFO,
crate::casts::UNNECESSARY_CAST_INFO,
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
@@ -136,12 +140,15 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::double_parens::DOUBLE_PARENS_INFO,
crate::drop_forget_ref::DROP_NON_DROP_INFO,
crate::drop_forget_ref::FORGET_NON_DROP_INFO,
- crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO,
+ crate::drop_forget_ref::MEM_FORGET_INFO,
crate::duplicate_mod::DUPLICATE_MOD_INFO,
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
crate::empty_drop::EMPTY_DROP_INFO,
crate::empty_enum::EMPTY_ENUM_INFO,
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
+ crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
+ crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
+ 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,
@@ -153,6 +160,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO,
crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO,
+ crate::excessive_nesting::EXCESSIVE_NESTING_INFO,
crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO,
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
crate::exit::EXIT_INFO,
@@ -198,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
+ crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
crate::indexing_slicing::INDEXING_SLICING_INFO,
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
@@ -212,7 +221,6 @@ 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::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_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,
@@ -221,6 +229,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::large_futures::LARGE_FUTURES_INFO,
crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
+ crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
crate::len_zero::LEN_ZERO_INFO,
@@ -268,6 +277,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
+ crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO,
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
crate::manual_retain::MANUAL_RETAIN_INFO,
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
@@ -301,7 +311,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::matches::TRY_ERR_INFO,
crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
- crate::mem_forget::MEM_FORGET_INFO,
crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO,
crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO,
@@ -316,6 +325,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::CLONE_ON_COPY_INFO,
crate::methods::CLONE_ON_REF_PTR_INFO,
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
+ crate::methods::DRAIN_COLLECT_INFO,
crate::methods::ERR_EXPECT_INFO,
crate::methods::EXPECT_FUN_CALL_INFO,
crate::methods::EXPECT_USED_INFO,
@@ -354,6 +364,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
crate::methods::MANUAL_SPLIT_ONCE_INFO,
crate::methods::MANUAL_STR_REPEAT_INFO,
+ crate::methods::MANUAL_TRY_FOLD_INFO,
crate::methods::MAP_CLONE_INFO,
crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
crate::methods::MAP_ERR_IGNORE_INFO,
@@ -400,6 +411,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::UNNECESSARY_FOLD_INFO,
crate::methods::UNNECESSARY_JOIN_INFO,
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
+ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
crate::methods::UNNECESSARY_SORT_BY_INFO,
crate::methods::UNNECESSARY_TO_OWNED_INFO,
crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO,
@@ -409,6 +421,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::VERBOSE_FILE_READS_INFO,
crate::methods::WRONG_SELF_CONVENTION_INFO,
crate::methods::ZST_OFFSET_INFO,
+ crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
crate::minmax::MIN_MAX_INFO,
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
crate::misc::TOPLEVEL_REF_ARG_INFO,
@@ -418,6 +431,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::misc_early::DOUBLE_NEG_INFO,
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO,
+ crate::misc_early::REDUNDANT_AT_REST_PATTERN_INFO,
crate::misc_early::REDUNDANT_PATTERN_INFO,
crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO,
@@ -429,6 +443,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
+ crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO,
crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,
crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO,
@@ -449,7 +464,9 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
+ crate::needless_else::NEEDLESS_ELSE_INFO,
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
+ crate::needless_if::NEEDLESS_IF_INFO,
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
@@ -476,7 +493,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
crate::operators::ASSIGN_OP_PATTERN_INFO,
crate::operators::BAD_BIT_MASK_INFO,
- crate::operators::CMP_NAN_INFO,
crate::operators::CMP_OWNED_INFO,
crate::operators::DOUBLE_COMPARISONS_INFO,
crate::operators::DURATION_SUBSEC_INFO,
@@ -525,6 +541,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ranges::RANGE_MINUS_ONE_INFO,
crate::ranges::RANGE_PLUS_ONE_INFO,
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
+ crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO,
+ crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
@@ -536,6 +554,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
+ crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO,
crate::ref_option_ref::REF_OPTION_REF_INFO,
crate::ref_patterns::REF_PATTERNS_INFO,
crate::reference::DEREF_ADDROF_INFO,
@@ -554,8 +573,10 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::shadow::SHADOW_SAME_INFO,
crate::shadow::SHADOW_UNRELATED_INFO,
crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
+ crate::single_call_fn::SINGLE_CALL_FN_INFO,
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
+ crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO,
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
crate::size_of_ref::SIZE_OF_REF_INFO,
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
@@ -603,6 +624,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO,
crate::transmute::USELESS_TRANSMUTE_INFO,
crate::transmute::WRONG_TRANSMUTE_INFO,
+ crate::tuple_array_conversions::TUPLE_ARRAY_CONVERSIONS_INFO,
crate::types::BORROWED_BOX_INFO,
crate::types::BOX_COLLECTION_INFO,
crate::types::LINKEDLIST_INFO,
@@ -645,6 +667,9 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::useless_conversion::USELESS_CONVERSION_INFO,
crate::vec::USELESS_VEC_INFO,
crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO,
+ crate::visibility::NEEDLESS_PUB_SELF_INFO,
+ crate::visibility::PUB_WITHOUT_SHORTHAND_INFO,
+ crate::visibility::PUB_WITH_SHORTHAND_INFO,
crate::wildcard_imports::ENUM_GLOB_USE_INFO,
crate::wildcard_imports::WILDCARD_IMPORTS_INFO,
crate::write::PRINTLN_EMPTY_STRING_INFO,
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 fb037bbcb..ca9514ccc 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
@@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
- /// Check for construction on unit struct using `default`.
+ /// Checks for construction on unit struct using `default`.
///
/// ### Why is this bad?
/// This adds code complexity and an unnecessary function call.
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 4e1a6cd4d..e53a9877b 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
let fields_def = &variant.fields;
// Push field type then visit each field expr.
- for field in fields.iter() {
+ for field in *fields {
let bound =
fields_def
.iter()
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index b27ffe73f..12f2f37e3 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -26,8 +26,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::{Rvalue, StatementKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{
- self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy,
- PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults,
+ self, Binder, BoundVariableKind, ClauseKind, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy,
+ ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults,
};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol};
@@ -56,9 +56,11 @@ declare_clippy_lint! {
/// let b = &*a;
/// ```
///
- /// This lint excludes:
+ /// This lint excludes all of:
/// ```rust,ignore
/// let _ = d.unwrap().deref();
+ /// let _ = Foo::deref(&foo);
+ /// let _ = <Foo as Deref>::deref(&foo);
/// ```
#[clippy::version = "1.44.0"]
pub EXPLICIT_DEREF_METHODS,
@@ -355,15 +357,17 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// start auto-deref.
// 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
// adjustments will not be inserted automatically, then leave one further reference to avoid
- // moving a mutable borrow.
- // e.g.
- // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
- // let x = match x {
- // // Removing the borrow will cause `x` to be moved
- // Some(x) => &mut *x,
- // None => y
- // };
- // }
+ // moving a mutable borrow. e.g.
+ //
+ // ```rust
+ // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
+ // let x = match x {
+ // // Removing the borrow will cause `x` to be moved
+ // Some(x) => &mut *x,
+ // None => y
+ // };
+ // }
+ // ```
let deref_msg =
"this expression creates a reference which is immediately dereferenced by the compiler";
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
@@ -1133,7 +1137,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
let projection_predicates = predicates
.iter()
.filter_map(|predicate| {
- if let PredicateKind::Clause(Clause::Projection(projection_predicate)) = predicate.kind().skip_binder() {
+ if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
Some(projection_predicate)
} else {
None
@@ -1147,7 +1151,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
if predicates
.iter()
.filter_map(|predicate| {
- if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
+ if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
{
Some(trait_predicate.trait_ref.def_id)
@@ -1209,7 +1213,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
}
predicates.iter().all(|predicate| {
- if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
+ if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& 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) = substs_with_referent_ty[param_ty.index as usize].unpack()
@@ -1219,7 +1223,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
return false;
}
- let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
+ let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, &substs_with_referent_ty);
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
let infcx = cx.tcx.infer_ctxt().build();
infcx.predicate_must_hold_modulo_regions(&obligation)
@@ -1292,8 +1296,8 @@ fn referent_used_exactly_once<'tcx>(
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
reference: &Expr<'tcx>,
) -> bool {
- let mir = enclosing_mir(cx.tcx, reference.hir_id);
- if let Some(local) = expr_local(cx.tcx, reference)
+ if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
+ && let Some(local) = expr_local(cx.tcx, reference)
&& let [location] = *local_assignments(mir, local).as_slice()
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
@@ -1424,6 +1428,7 @@ fn ty_auto_deref_stability<'tcx>(
continue;
},
ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
+ ty::Alias(ty::Weak, _) => unreachable!("should have been normalized away above"),
ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"),
ty::Alias(ty::Projection, _) if ty.has_non_region_param() => {
TyPosition::new_deref_stable_for_result(precedence, ty)
@@ -1480,7 +1485,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
target_mut,
} => {
let mut app = Applicability::MachineApplicable;
- let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+ let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
let ty = cx.typeck_results().expr_ty(expr);
let (_, ref_count) = peel_mid_ty_refs(ty);
let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
@@ -1503,11 +1508,20 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
"&"
};
- let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
- format!("({expr_str})")
+ // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
+ // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
+ /*
+ expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
+ Cow::Owned(format!("({expr_str})"))
} else {
- expr_str.into_owned()
+ expr_str
};
+ */
+
+ // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`.
+ if is_final_ufcs {
+ return;
+ }
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 8f68f90a2..020ffe7f8 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -4,11 +4,13 @@ use clippy_utils::source::indent_of;
use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{
+ self as hir,
def::{CtorKind, CtorOf, DefKind, Res},
- Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
+ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
+use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
+use rustc_middle::ty::{self, Adt, AdtDef, SubstsRef, Ty, TypeckResults};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
@@ -75,13 +77,23 @@ fn is_path_self(e: &Expr<'_>) -> bool {
}
}
+fn contains_trait_object(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Ref(_, ty, _) => contains_trait_object(*ty),
+ ty::Adt(def, substs) => def.is_box() && substs[0].as_type().map_or(false, contains_trait_object),
+ ty::Dynamic(..) => true,
+ _ => false,
+ }
+}
+
fn check_struct<'tcx>(
cx: &LateContext<'tcx>,
item: &'tcx Item<'_>,
- self_ty: &Ty<'_>,
+ self_ty: &hir::Ty<'_>,
func_expr: &Expr<'_>,
adt_def: AdtDef<'_>,
substs: SubstsRef<'_>,
+ typeck_results: &'tcx TypeckResults<'tcx>,
) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args, .. }) = p.segments.last() {
@@ -96,10 +108,23 @@ fn check_struct<'tcx>(
}
}
}
+
+ // the default() call might unsize coerce to a trait object (e.g. Box<T> to Box<dyn Trait>),
+ // which would not be the same if derived (see #10158).
+ // this closure checks both if the expr is equivalent to a `default()` call and does not
+ // have such coercions.
+ let is_default_without_adjusts = |expr| {
+ is_default_equivalent(cx, expr)
+ && typeck_results.expr_adjustments(expr).iter().all(|adj| {
+ !matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)
+ if contains_trait_object(adj.target))
+ })
+ };
+
let should_emit = match peel_blocks(func_expr).kind {
- ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
- ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
- ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
+ ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts),
+ ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts),
+ ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)),
_ => false,
};
@@ -197,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
then {
if adt_def.is_struct() {
- check_struct(cx, item, self_ty, func_expr, adt_def, substs);
+ check_struct(cx, item, self_ty, func_expr, adt_def, substs, cx.tcx.typeck_body(*b));
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
check_enum(cx, item, func_expr, adt_def);
}
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 8f5d319cd..a005a360e 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -14,7 +14,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
- self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind,
+ self, BoundConstness, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate,
TraitPredicate, Ty, TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -503,7 +503,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
let ty_predicates = tcx.predicates_of(did).predicates;
for (p, _) in ty_predicates {
- if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder()
+ if let ClauseKind::Trait(p) = p.kind().skip_binder()
&& p.trait_ref.def_id == eq_trait_id
&& let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
&& p.constness == BoundConstness::NotConst
@@ -514,13 +514,14 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
}
ParamEnv::new(
- tcx.mk_predicates_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain(
+ tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain(
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
- tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate {
+ ClauseKind::Trait(TraitPredicate {
trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]),
constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive,
- }))))
+ })
+ .to_predicate(tcx)
}),
)),
Reveal::UserFacing,
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index 384aca7fe..87d88f707 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -571,6 +571,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
let mut in_link = None;
let mut in_heading = false;
let mut is_rust = false;
+ let mut no_test = false;
let mut edition = None;
let mut ticks_unbalanced = false;
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
@@ -584,6 +585,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
if item == "ignore" {
is_rust = false;
break;
+ } else if item == "no_test" {
+ no_test = true;
}
if let Some(stripped) = item.strip_prefix("edition") {
is_rust = true;
@@ -648,7 +651,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers.errors |= in_heading && trimmed_text == "Errors";
headers.panics |= in_heading && trimmed_text == "Panics";
if in_code {
- if is_rust {
+ if is_rust && !no_test {
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
check_code(cx, &text, edition, span);
}
@@ -906,15 +909,15 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
if is_panic(self.cx, macro_call.def_id)
|| matches!(
self.cx.tcx.item_name(macro_call.def_id).as_str(),
- "assert" | "assert_eq" | "assert_ne" | "todo"
+ "assert" | "assert_eq" | "assert_ne"
)
{
self.panic_span = Some(macro_call.span);
}
}
- // check for `unwrap`
- if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
+ // check for `unwrap` and `expect` for both `Option` and `Result`
+ if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
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 9c60edb17..976ce47e8 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::get_parent_node;
use clippy_utils::is_must_use_func_call;
use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
@@ -6,6 +6,7 @@ use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
+use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@@ -49,31 +50,23 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
+ /// Checks for usage of `std::mem::forget(t)` where `t` is
+ /// `Drop` or has a field that implements `Drop`.
///
/// ### Why is this bad?
- /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
- ///
- /// ### Known problems
- /// Does not catch cases if the user binds `std::mem::drop`
- /// to a different name and calls it that way.
+ /// `std::mem::forget(t)` prevents `t` from running its
+ /// destructor, possibly causing leaks.
///
/// ### Example
/// ```rust
- /// struct S;
- /// drop(std::mem::ManuallyDrop::new(S));
- /// ```
- /// Use instead:
- /// ```rust
- /// struct S;
- /// unsafe {
- /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
- /// }
+ /// # use std::mem;
+ /// # use std::rc::Rc;
+ /// mem::forget(Rc::new(55))
/// ```
- #[clippy::version = "1.49.0"]
- pub UNDROPPED_MANUALLY_DROPS,
- correctness,
- "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
+ #[clippy::version = "pre 1.29.0"]
+ pub MEM_FORGET,
+ restriction,
+ "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
}
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
@@ -84,7 +77,7 @@ const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value t
declare_lint_pass!(DropForgetRef => [
DROP_NON_DROP,
FORGET_NON_DROP,
- UNDROPPED_MANUALLY_DROPS
+ MEM_FORGET,
]);
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
@@ -97,23 +90,13 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
let arg_ty = cx.typeck_results().expr_ty(arg);
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) = match fn_name {
+ let (lint, msg, note_span) = match fn_name {
// 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,
sym::mem_forget if is_copy => return,
- sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
- span_lint_and_help(
- cx,
- UNDROPPED_MANUALLY_DROPS,
- expr.span,
- "the inner value of this ManuallyDrop will not be dropped",
- None,
- "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
- );
- return;
- }
+ sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return,
sym::mem_drop
if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|| is_must_use_func_call(cx, arg)
@@ -121,19 +104,34 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|| drop_is_single_call_in_arm
) =>
{
- (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
- },
- sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
- (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
+ (DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
},
+ sym::mem_forget => {
+ if arg_ty.needs_drop(cx.tcx, cx.param_env) {
+ (
+ MEM_FORGET,
+ Cow::Owned(format!(
+ "usage of `mem::forget` on {}",
+ if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
+ "`Drop` type"
+ } else {
+ "type with `Drop` fields"
+ }
+ )),
+ None,
+ )
+ } else {
+ (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY.into(), Some(arg.span))
+ }
+ }
_ => return,
};
span_lint_and_note(
cx,
lint,
expr.span,
- msg,
- Some(arg.span),
+ &msg,
+ note_span,
&format!("argument has type `{arg_ty}`"),
);
}
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
new file mode 100644
index 000000000..f47098783
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -0,0 +1,216 @@
+use crate::Lint;
+use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::{lint::in_external_macro, ty::Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Symbol;
+use std::borrow::Cow;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
+ ///
+ /// ### Why is this bad?
+ /// It's not, but some may prefer to specify the target endianness explicitly.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let _x = 2i32.to_ne_bytes();
+ /// let _y = 2i64.to_ne_bytes();
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub HOST_ENDIAN_BYTES,
+ restriction,
+ "disallows usage of the `to_ne_bytes` method"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
+ ///
+ /// ### Why is this bad?
+ /// It's not, but some may wish to lint usage of this method, either to suggest using the host
+ /// endianness or big endian.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let _x = 2i32.to_le_bytes();
+ /// let _y = 2i64.to_le_bytes();
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub LITTLE_ENDIAN_BYTES,
+ restriction,
+ "disallows usage of the `to_le_bytes` method"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
+ ///
+ /// ### Why is this bad?
+ /// It's not, but some may wish to lint usage of this method, either to suggest using the host
+ /// endianness or little endian.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let _x = 2i32.to_be_bytes();
+ /// let _y = 2i64.to_be_bytes();
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub BIG_ENDIAN_BYTES,
+ restriction,
+ "disallows usage of the `to_be_bytes` method"
+}
+
+declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
+
+const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
+const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
+const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
+
+#[derive(Clone, Debug)]
+enum LintKind {
+ Host,
+ Little,
+ Big,
+}
+
+#[derive(Clone, Copy, PartialEq)]
+enum Prefix {
+ From,
+ To,
+}
+
+impl LintKind {
+ fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ is_lint_allowed(cx, self.as_lint(), expr.hir_id)
+ }
+
+ fn as_lint(&self) -> &'static Lint {
+ match self {
+ LintKind::Host => HOST_ENDIAN_BYTES,
+ LintKind::Little => LITTLE_ENDIAN_BYTES,
+ LintKind::Big => BIG_ENDIAN_BYTES,
+ }
+ }
+
+ fn as_name(&self, prefix: Prefix) -> &str {
+ let index = usize::from(prefix == Prefix::To);
+
+ match self {
+ LintKind::Host => HOST_NAMES[index],
+ LintKind::Little => LITTLE_NAMES[index],
+ LintKind::Big => BIG_NAMES[index],
+ }
+ }
+}
+
+impl LateLintPass<'_> for EndianBytes {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind;
+ if args.is_empty();
+ let ty = cx.typeck_results().expr_ty(receiver);
+ if ty.is_primitive_ty();
+ if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty);
+ then {
+ return;
+ }
+ }
+
+ if_chain! {
+ if let ExprKind::Call(function, ..) = expr.kind;
+ if let ExprKind::Path(qpath) = function.kind;
+ if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id();
+ if let Some(function_name) = cx.get_def_path(def_id).last();
+ let ty = cx.typeck_results().expr_ty(expr);
+ if ty.is_primitive_ty();
+ then {
+ maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
+ }
+ }
+ }
+}
+
+fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {
+ let ne = LintKind::Host.as_name(prefix);
+ let le = LintKind::Little.as_name(prefix);
+ let be = LintKind::Big.as_name(prefix);
+
+ let (lint, other_lints) = match name.as_str() {
+ name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
+ name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
+ name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
+ _ => return false,
+ };
+
+ let mut help = None;
+
+ 'build_help: {
+ // all lints disallowed, don't give help here
+ if [&[lint], other_lints.as_slice()]
+ .concat()
+ .iter()
+ .all(|lint| !lint.allowed(cx, expr))
+ {
+ break 'build_help;
+ }
+
+ // ne_bytes and all other lints allowed
+ if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
+ help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
+ break 'build_help;
+ }
+
+ // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
+ // le_bytes is not
+ if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
+ help = Some(Cow::Borrowed("use the native endianness instead"));
+ break 'build_help;
+ }
+
+ let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
+ let len = allowed_lints.clone().count();
+
+ let mut help_str = "use ".to_owned();
+
+ for (i, lint) in allowed_lints.enumerate() {
+ let only_one = len == 1;
+ if !only_one {
+ help_str.push_str("either of ");
+ }
+
+ help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
+
+ if i != len && !only_one {
+ help_str.push_str("or ");
+ }
+ }
+
+ help = Some(Cow::Owned(help_str + "instead"));
+ }
+
+ span_lint_and_then(
+ cx,
+ lint.as_lint(),
+ expr.span,
+ &format!(
+ "usage of the {}`{ty}::{}`{}",
+ if prefix == Prefix::From { "function " } else { "" },
+ lint.as_name(prefix),
+ if prefix == Prefix::To { " method" } else { "" },
+ ),
+ move |diag| {
+ if let Some(help) = help {
+ diag.help(help);
+ }
+ },
+ );
+
+ true
+}
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index e275efaba..d85650712 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id())
.ok()
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty));
- if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
+ if let Some(Constant::Int(val)) = constant.and_then(|c| miri_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/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs
index faac63404..d4df6f7aa 100644
--- a/src/tools/clippy/clippy_lints/src/enum_variants.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs
@@ -3,7 +3,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
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, Variant};
+use rustc_hir::{EnumDef, Item, ItemKind, OwnerId, Variant};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
@@ -105,18 +105,20 @@ declare_clippy_lint! {
}
pub struct EnumVariantNames {
- modules: Vec<(Symbol, String)>,
+ modules: Vec<(Symbol, String, OwnerId)>,
threshold: u64,
avoid_breaking_exported_api: bool,
+ allow_private_module_inception: bool,
}
impl EnumVariantNames {
#[must_use]
- pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
+ pub fn new(threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool) -> Self {
Self {
modules: Vec::new(),
threshold,
avoid_breaking_exported_api,
+ allow_private_module_inception,
}
}
}
@@ -252,18 +254,19 @@ impl LateLintPass<'_> for EnumVariantNames {
let item_name = item.ident.name.as_str();
let item_camel = to_camel_case(item_name);
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
- if let Some((mod_name, mod_camel)) = self.modules.last() {
+ if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules {
// constants don't have surrounding modules
if !mod_camel.is_empty() {
- if mod_name == &item.ident.name {
- if let ItemKind::Mod(..) = item.kind {
- span_lint(
- cx,
- MODULE_INCEPTION,
- item.span,
- "module has the same name as its containing module",
- );
- }
+ if mod_name == &item.ident.name
+ && let ItemKind::Mod(..) = item.kind
+ && (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public())
+ {
+ span_lint(
+ cx,
+ MODULE_INCEPTION,
+ item.span,
+ "module has the same name as its containing module",
+ );
}
// The `module_name_repetitions` lint should only trigger if the item has the module in its
// name. Having the same name is accepted.
@@ -302,6 +305,6 @@ impl LateLintPass<'_> for EnumVariantNames {
check_variant(cx, self.threshold, def, item_name, item.span);
}
}
- self.modules.push((item.ident.name, item_camel));
+ self.modules.push((item.ident.name, item_camel, item.owner_id));
}
}
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index b2071f4dc..58e62d1f3 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -120,6 +120,13 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
if let ty::Closure(_, substs) = *closure_ty.kind();
+ // Don't lint if this is an inclusive range expression.
+ // They desugar to a call to `RangeInclusiveNew` which would have odd suggestions. (#10684)
+ if !matches!(higher::Range::hir(body.value), Some(higher::Range {
+ start: Some(_),
+ end: Some(_),
+ limits: rustc_ast::RangeLimits::Closed
+ }));
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
@@ -136,6 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
// 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",
@@ -243,7 +251,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs
| ty::Ref(..)
| ty::Slice(_)
| ty::Tuple(_) => {
- format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs))
+ format!("<{}>", EarlyBinder::bind(ty).subst(cx.tcx, substs))
},
_ => ty.to_string(),
}
diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
new file mode 100644
index 000000000..d04d833e6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
@@ -0,0 +1,181 @@
+use clippy_utils::{diagnostics::span_lint_and_help, source::snippet};
+use rustc_ast::{
+ node_id::NodeSet,
+ visit::{walk_block, walk_item, Visitor},
+ Block, Crate, Inline, Item, ItemKind, ModKind, NodeId,
+};
+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::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for blocks which are nested beyond a certain threshold.
+ ///
+ /// Note: Even though this lint is warn-by-default, it will only trigger if a maximum nesting level is defined in the clippy.toml file.
+ ///
+ /// ### Why is this bad?
+ /// It can severely hinder readability.
+ ///
+ /// ### Example
+ /// An example clippy.toml configuration:
+ /// ```toml
+ /// # clippy.toml
+ /// excessive-nesting-threshold = 3
+ /// ```
+ /// ```rust,ignore
+ /// // lib.rs
+ /// pub mod a {
+ /// pub struct X;
+ /// impl X {
+ /// pub fn run(&self) {
+ /// if true {
+ /// // etc...
+ /// }
+ /// }
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// // a.rs
+ /// fn private_run(x: &X) {
+ /// if true {
+ /// // etc...
+ /// }
+ /// }
+ ///
+ /// pub struct X;
+ /// impl X {
+ /// pub fn run(&self) {
+ /// private_run(self);
+ /// }
+ /// }
+ /// ```
+ /// ```rust,ignore
+ /// // lib.rs
+ /// pub mod a;
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub EXCESSIVE_NESTING,
+ complexity,
+ "checks for blocks nested beyond a certain threshold"
+}
+impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
+
+#[derive(Clone)]
+pub struct ExcessiveNesting {
+ pub excessive_nesting_threshold: u64,
+ pub nodes: NodeSet,
+}
+
+impl ExcessiveNesting {
+ pub fn check_node_id(&self, cx: &EarlyContext<'_>, span: Span, node_id: NodeId) {
+ if self.nodes.contains(&node_id) {
+ span_lint_and_help(
+ cx,
+ EXCESSIVE_NESTING,
+ span,
+ "this block is too nested",
+ None,
+ "try refactoring your code to minimize nesting",
+ );
+ }
+ }
+}
+
+impl EarlyLintPass for ExcessiveNesting {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
+ if self.excessive_nesting_threshold == 0 {
+ return;
+ }
+
+ let mut visitor = NestingVisitor {
+ conf: self,
+ cx,
+ nest_level: 0,
+ };
+
+ for item in &krate.items {
+ visitor.visit_item(item);
+ }
+ }
+
+ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
+ self.check_node_id(cx, block.span, block.id);
+ }
+
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ self.check_node_id(cx, item.span, item.id);
+ }
+}
+
+struct NestingVisitor<'conf, 'cx> {
+ conf: &'conf mut ExcessiveNesting,
+ cx: &'cx EarlyContext<'cx>,
+ nest_level: u64,
+}
+
+impl NestingVisitor<'_, '_> {
+ fn check_indent(&mut self, span: Span, id: NodeId) -> bool {
+ if self.nest_level > self.conf.excessive_nesting_threshold && !in_external_macro(self.cx.sess(), span) {
+ self.conf.nodes.insert(id);
+
+ return true;
+ }
+
+ false
+ }
+}
+
+impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> {
+ fn visit_block(&mut self, block: &Block) {
+ if block.span.from_expansion() {
+ return;
+ }
+
+ // TODO: This should be rewritten using `LateLintPass` so we can use `is_from_proc_macro` instead,
+ // but for now, this is fine.
+ let snippet = snippet(self.cx, block.span, "{}").trim().to_owned();
+ if !snippet.starts_with('{') || !snippet.ends_with('}') {
+ return;
+ }
+
+ self.nest_level += 1;
+
+ if !self.check_indent(block.span, block.id) {
+ walk_block(self, block);
+ }
+
+ self.nest_level -= 1;
+ }
+
+ fn visit_item(&mut self, item: &Item) {
+ if item.span.from_expansion() {
+ return;
+ }
+
+ match &item.kind {
+ ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
+ self.nest_level += 1;
+
+ if !self.check_indent(item.span, item.id) {
+ walk_item(self, item);
+ }
+
+ self.nest_level -= 1;
+ },
+ // Reset nesting level for non-inline modules (since these are in another file)
+ ItemKind::Mod(..) => walk_item(
+ &mut NestingVisitor {
+ conf: self.conf,
+ cx: self.cx,
+ nest_level: 0,
+ },
+ item,
+ ),
+ _ => walk_item(self, item),
+ }
+ }
+}
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 eeb4de8b5..126bed678 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
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
+use clippy_utils::is_from_proc_macro;
use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
@@ -265,6 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(_, generics, body_id) = item.kind
&& !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
+ && !is_from_proc_macro(cx, item)
{
let mut walker = TypeWalker::new(cx, generics);
walk_item(&mut walker, item);
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index 93bf50fd5..d182bb621 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -82,19 +82,24 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
LitFloatType::Unsuffixed => None
};
- let (is_whole, mut float_str) = match fty {
+ let (is_whole, is_inf, mut float_str) = match fty {
FloatTy::F32 => {
let value = sym_str.parse::<f32>().unwrap();
- (value.fract() == 0.0, formatter.format(value))
+ (value.fract() == 0.0, value.is_infinite(), formatter.format(value))
},
FloatTy::F64 => {
let value = sym_str.parse::<f64>().unwrap();
- (value.fract() == 0.0, formatter.format(value))
+
+ (value.fract() == 0.0, value.is_infinite(), formatter.format(value))
},
};
+ if is_inf {
+ return;
+ }
+
if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
// Normalize the literal by stripping the fractional portion
if sym_str.split('.').next().unwrap() != float_str {
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 3c55a563a..5e0fcd743 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -215,7 +215,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
// this range are lossy and ambiguous.
#[expect(clippy::cast_possible_truncation)]
-fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
+fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
match value {
F32(num) if num.fract() == 0.0 => {
if (-16_777_215.0..16_777_216.0).contains(num) {
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 68c5c3673..45f67020c 100644
--- a/src/tools/clippy/clippy_lints/src/format_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
-use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
+use clippy_utils::{higher, match_def_path, paths};
+use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -44,10 +44,24 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String)
}
fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
+ let e = e.peel_blocks().peel_borrows();
+
+ if e.span.from_expansion()
+ && let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id
+ {
cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
+ } else if let Some(higher::If { then, r#else, .. }) = higher::If::hir(e) {
+ is_format(cx, then) || r#else.is_some_and(|e| is_format(cx, e))
} else {
- false
+ match higher::IfLetOrMatch::parse(cx, e) {
+ Some(higher::IfLetOrMatch::Match(_, arms, MatchSource::Normal)) => {
+ 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))
+ },
+ _ => false,
+ }
}
}
@@ -68,7 +82,6 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
},
_ => return,
};
- let (arg, _) = peel_hir_expr_refs(arg);
if is_format(cx, arg) {
span_lint_and_help(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index 4762b3543..d03480c21 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -236,6 +236,12 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
}
}
+ // Don't warn if the only thing inside post_else_post_eol is a comment block.
+ let trimmed_post_else_post_eol = post_else_post_eol.trim();
+ if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") {
+ return
+ }
+
let else_desc = if is_if(else_) { "if" } else { "{..}" };
span_lint_and_note(
cx,
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 10ce2a0f0..92d67ef35 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -134,9 +134,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
kw::SelfUpper => self.upper.push(segment.ident.span),
_ => continue,
}
+
+ self.invalid |= segment.ident.span.from_expansion();
}
- self.invalid |= path.span.from_expansion();
if !self.invalid {
walk_path(self, path);
}
@@ -156,6 +157,11 @@ fn convert_to_from(
self_ty: &Ty<'_>,
impl_item_ref: &ImplItemRef,
) -> Option<Vec<(Span, String)>> {
+ if !target_ty.find_self_aliases().is_empty() {
+ // It's tricky to expand self-aliases correctly, we'll ignore it to not cause a
+ // bad suggestion/fix.
+ return None;
+ }
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None };
let body = cx.tcx.hir().body(body_id);
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 d1314795f..818ebd113 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, AliasTy, Clause, PredicateKind};
+use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
@@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
let preds = cx.tcx.explicit_item_bounds(def_id);
let mut is_future = false;
for (p, _span) in preds.subst_iter_copied(cx.tcx, substs) {
- if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
+ if let Some(trait_pred) = p.as_trait_clause() {
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
is_future = true;
break;
@@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
infcx
.err_ctxt()
.maybe_note_obligation_cause_for_async_await(db, &obligation);
- if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
+ if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) =
obligation.predicate.kind().skip_binder()
{
db.note(format!(
diff --git a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs b/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
new file mode 100644
index 000000000..7b95116ee
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
@@ -0,0 +1,124 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait};
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::EarlyBinder;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, symbol};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual implementations of `Clone` when `Copy` is already implemented.
+ ///
+ /// ### Why is this bad?
+ /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing
+ /// `self` in `Clone`'s implementation. Anything else is incorrect.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[derive(Eq, PartialEq)]
+ /// struct A(u32);
+ ///
+ /// impl Clone for A {
+ /// fn clone(&self) -> Self {
+ /// Self(self.0)
+ /// }
+ /// }
+ ///
+ /// impl Copy for A {}
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// #[derive(Eq, PartialEq)]
+ /// struct A(u32);
+ ///
+ /// impl Clone for A {
+ /// fn clone(&self) -> Self {
+ /// *self
+ /// }
+ /// }
+ ///
+ /// impl Copy for A {}
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
+ correctness,
+ "manual implementation of `Clone` on a `Copy` type"
+}
+declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]);
+
+impl LateLintPass<'_> for IncorrectImpls {
+ #[expect(clippy::needless_return)]
+ fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
+ let node = get_parent_node(cx.tcx, impl_item.hir_id());
+ let Some(Node::Item(item)) = node else {
+ return;
+ };
+ let ItemKind::Impl(imp) = item.kind else {
+ return;
+ };
+ let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else {
+ return;
+ };
+ let trait_impl_def_id = trait_impl.def_id;
+ if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
+ return;
+ }
+ let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
+ return;
+ };
+ let body = cx.tcx.hir().body(impl_item_id);
+ let ExprKind::Block(block, ..) = body.value.kind else {
+ return;
+ };
+ // Above is duplicated from the `duplicate_manual_partial_ord_impl` branch.
+ // Remove it while solving conflicts once that PR is merged.
+
+ // Actual implementation; remove this comment once aforementioned PR is merged
+ 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,
+ hir_ty_to_ty(cx.tcx, imp.self_ty),
+ copy_def_id,
+ &[],
+ )
+ {
+ if impl_item.ident.name == sym::clone {
+ if block.stmts.is_empty()
+ && let Some(expr) = block.expr
+ && let ExprKind::Unary(UnOp::Deref, inner) = expr.kind
+ && let ExprKind::Path(qpath) = inner.kind
+ && last_path_segment(&qpath).ident.name == symbol::kw::SelfLower
+ {} else {
+ span_lint_and_sugg(
+ cx,
+ INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
+ block.span,
+ "incorrect implementation of `clone` on a `Copy` type",
+ "change this to",
+ "{ *self }".to_owned(),
+ Applicability::MaybeIncorrect,
+ );
+
+ return;
+ }
+ }
+
+ if impl_item.ident.name == sym::clone_from {
+ span_lint_and_sugg(
+ cx,
+ INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
+ impl_item.span,
+ "incorrect implementation of `clone_from` on a `Copy` type",
+ "remove this",
+ String::new(),
+ Applicability::MaybeIncorrect,
+ );
+
+ return;
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs b/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
deleted file mode 100644
index 6a4861747..000000000
--- a/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_function_call, paths};
-use rustc_ast::{BorrowKind, LitKind};
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-use rustc_span::Span;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
- ///
- /// ### Why is this bad?
- /// Creating such a `str` would result in undefined behavior
- ///
- /// ### Example
- /// ```rust
- /// # #[allow(unused)]
- /// unsafe {
- /// std::str::from_utf8_unchecked(b"cl\x82ippy");
- /// }
- /// ```
- #[clippy::version = "1.64.0"]
- pub INVALID_UTF8_IN_UNCHECKED,
- correctness,
- "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
-}
-declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
-
-impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
- match &arg.kind {
- ExprKind::Lit(Spanned { node: lit, .. }) => {
- if let LitKind::ByteStr(bytes, _) = &lit
- && std::str::from_utf8(bytes).is_err()
- {
- lint(cx, expr.span);
- }
- },
- ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
- let elements = args.iter().map(|e|{
- match &e.kind {
- ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
- LitKind::Byte(b) => Some(*b),
- #[allow(clippy::cast_possible_truncation)]
- LitKind::Int(b, _) => Some(*b as u8),
- _ => None
- }
- _ => None
- }
- }).collect::<Option<Vec<_>>>();
-
- if let Some(elements) = elements
- && std::str::from_utf8(&elements).is_err()
- {
- lint(cx, expr.span);
- }
- }
- _ => {}
- }
- }
- }
-}
-
-fn lint(cx: &LateContext<'_>, span: Span) {
- span_lint(
- cx,
- INVALID_UTF8_IN_UNCHECKED,
- span,
- "non UTF-8 literal in `std::str::from_utf8_unchecked`",
- );
-}
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 0ca31033b..087c4a652 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -38,7 +38,7 @@ declare_clippy_lint! {
/// wait(fut).await;
/// }
/// ```
- #[clippy::version = "1.68.0"]
+ #[clippy::version = "1.70.0"]
pub LARGE_FUTURES,
pedantic,
"large future may lead to unexpected stack overflows"
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 32c6312e0..0a5901bce 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -38,7 +38,7 @@ 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(_, _) = expr.kind
+ 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)
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
new file mode 100644
index 000000000..9c0cc978a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
@@ -0,0 +1,162 @@
+use std::ops::AddAssign;
+
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::fn_has_unsatisfiable_preds;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::Body;
+use rustc_hir::FnDecl;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_tool_lint;
+use rustc_session::impl_lint_pass;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for functions that use a lot of stack space.
+ ///
+ /// This often happens when constructing a large type, such as an array with a lot of elements,
+ /// or constructing *many* smaller-but-still-large structs, or copying around a lot of large types.
+ ///
+ /// This lint is a more general version of [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/#large_stack_arrays)
+ /// that is intended to look at functions as a whole instead of only individual array expressions inside of a function.
+ ///
+ /// ### Why is this bad?
+ /// The stack region of memory is very limited in size (usually *much* smaller than the heap) and attempting to
+ /// use too much will result in a stack overflow and crash the program.
+ /// To avoid this, you should consider allocating large types on the heap instead (e.g. by boxing them).
+ ///
+ /// Keep in mind that the code path to construction of large types does not even need to be reachable;
+ /// it purely needs to *exist* inside of the function to contribute to the stack size.
+ /// For example, this causes a stack overflow even though the branch is unreachable:
+ /// ```rust,ignore
+ /// fn main() {
+ /// if false {
+ /// let x = [0u8; 10000000]; // 10 MB stack array
+ /// black_box(&x);
+ /// }
+ /// }
+ /// ```
+ ///
+ /// ### Known issues
+ /// False positives. The stack size that clippy sees is an estimated value and can be vastly different
+ /// from the actual stack usage after optimizations passes have run (especially true in release mode).
+ /// Modern compilers are very smart and are able to optimize away a lot of unnecessary stack allocations.
+ /// In debug mode however, it is usually more accurate.
+ ///
+ /// This lint works by summing up the size of all variables that the user typed, variables that were
+ /// implicitly introduced by the compiler for temporaries, function arguments and the return value,
+ /// and comparing them against a (configurable, but high-by-default).
+ ///
+ /// ### 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
+ /// struct QuiteLargeType([u8; 500_000]);
+ /// fn foo() {
+ /// // ... some function that uses a lot of stack space ...
+ /// let _x1 = QuiteLargeType([0; 500_000]);
+ /// let _x2 = QuiteLargeType([0; 500_000]);
+ /// let _x3 = QuiteLargeType([0; 500_000]);
+ /// let _x4 = QuiteLargeType([0; 500_000]);
+ /// }
+ /// ```
+ ///
+ /// 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
+ /// struct NotSoLargeType(Box<[u8]>);
+ ///
+ /// fn foo() {
+ /// let _x1 = NotSoLargeType(vec![0; 500_000].into_boxed_slice());
+ /// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now heap allocated.
+ /// // The size of `NotSoLargeType` is 16 bytes.
+ /// // ...
+ /// }
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub LARGE_STACK_FRAMES,
+ nursery,
+ "checks for functions that allocate a lot of stack space"
+}
+
+pub struct LargeStackFrames {
+ maximum_allowed_size: u64,
+}
+
+impl LargeStackFrames {
+ #[must_use]
+ pub fn new(size: u64) -> Self {
+ Self {
+ maximum_allowed_size: size,
+ }
+ }
+}
+
+impl_lint_pass!(LargeStackFrames => [LARGE_STACK_FRAMES]);
+
+#[derive(Copy, Clone)]
+enum Space {
+ Used(u64),
+ Overflow,
+}
+
+impl Space {
+ pub fn exceeds_limit(self, limit: u64) -> bool {
+ match self {
+ Self::Used(used) => used > limit,
+ Self::Overflow => true,
+ }
+ }
+}
+
+impl AddAssign<u64> for Space {
+ fn add_assign(&mut self, rhs: u64) {
+ if let Self::Used(lhs) = self {
+ match lhs.checked_add(rhs) {
+ Some(sum) => *self = Self::Used(sum),
+ None => *self = Self::Overflow,
+ }
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ _: FnKind<'tcx>,
+ _: &'tcx FnDecl<'tcx>,
+ _: &'tcx Body<'tcx>,
+ span: Span,
+ local_def_id: LocalDefId,
+ ) {
+ let def_id = local_def_id.to_def_id();
+ // Building MIR for `fn`s with unsatisfiable preds results in ICE.
+ if fn_has_unsatisfiable_preds(cx, def_id) {
+ return;
+ }
+
+ let mir = cx.tcx.optimized_mir(def_id);
+ let param_env = cx.tcx.param_env(def_id);
+
+ let mut frame_size = Space::Used(0);
+
+ for local in &mir.local_decls {
+ if let Ok(layout) = cx.tcx.layout_of(param_env.and(local.ty)) {
+ frame_size += layout.size.bytes();
+ }
+ }
+
+ if frame_size.exceeds_limit(self.maximum_allowed_size) {
+ span_lint_and_note(
+ cx,
+ LARGE_STACK_FRAMES,
+ span,
+ "this function allocates a large amount of stack space",
+ None,
+ "allocating large amounts of stack space can overflow the stack",
+ );
+ }
+ }
+}
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 a677fcc41..4e9d77ea1 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
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// ```rust,ignore
/// let my_number = 1;
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub LET_WITH_TYPE_UNDERSCORE,
complexity,
"unneeded underscore type (`_`) in a variable declaration"
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index b442a4ac5..87329ee5e 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -68,6 +68,7 @@ mod renamed_lints;
mod allow_attributes;
mod almost_complete_range;
mod approx_const;
+mod arc_with_non_send_sync;
mod as_conversions;
mod asm_syntax;
mod assertions_on_constants;
@@ -114,6 +115,7 @@ mod else_if_without_else;
mod empty_drop;
mod empty_enum;
mod empty_structs_with_brackets;
+mod endian_bytes;
mod entry;
mod enum_clike;
mod enum_variants;
@@ -121,6 +123,7 @@ mod equatable_if_let;
mod escape;
mod eta_reduction;
mod excessive_bools;
+mod excessive_nesting;
mod exhaustive_items;
mod exit;
mod explicit_write;
@@ -147,6 +150,7 @@ mod implicit_return;
mod implicit_saturating_add;
mod implicit_saturating_sub;
mod inconsistent_struct_constructor;
+mod incorrect_impls;
mod index_refutable_slice;
mod indexing_slicing;
mod infinite_iter;
@@ -157,7 +161,6 @@ mod inline_fn_without_body;
mod instant_subtraction;
mod int_plus_one;
mod invalid_upcast_comparisons;
-mod invalid_utf8_in_unchecked;
mod items_after_statements;
mod items_after_test_module;
mod iter_not_returning_iterator;
@@ -166,6 +169,7 @@ mod large_enum_variant;
mod large_futures;
mod large_include_file;
mod large_stack_arrays;
+mod large_stack_frames;
mod len_zero;
mod let_if_seq;
mod let_underscore;
@@ -184,6 +188,7 @@ mod manual_is_ascii_check;
mod manual_let_else;
mod manual_main_separator_str;
mod manual_non_exhaustive;
+mod manual_range_patterns;
mod manual_rem_euclid;
mod manual_retain;
mod manual_slice_size_calculation;
@@ -192,9 +197,9 @@ mod manual_strip;
mod map_unit_fn;
mod match_result_ok;
mod matches;
-mod mem_forget;
mod mem_replace;
mod methods;
+mod min_ident_chars;
mod minmax;
mod misc;
mod misc_early;
@@ -203,6 +208,7 @@ mod missing_assert_message;
mod missing_const_for_fn;
mod missing_doc;
mod missing_enforced_import_rename;
+mod missing_fields_in_debug;
mod missing_inline;
mod missing_trait_methods;
mod mixed_read_write_in_expression;
@@ -218,7 +224,9 @@ mod needless_arbitrary_self_type;
mod needless_bool;
mod needless_borrowed_ref;
mod needless_continue;
+mod needless_else;
mod needless_for_each;
+mod needless_if;
mod needless_late_init;
mod needless_parens_on_range_literals;
mod needless_pass_by_value;
@@ -255,6 +263,7 @@ mod pub_use;
mod question_mark;
mod question_mark_used;
mod ranges;
+mod raw_strings;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
mod redundant_async_block;
@@ -265,6 +274,7 @@ mod redundant_field_names;
mod redundant_pub_crate;
mod redundant_slicing;
mod redundant_static_lifetimes;
+mod redundant_type_annotations;
mod ref_option_ref;
mod ref_patterns;
mod reference;
@@ -278,8 +288,10 @@ mod semicolon_if_nothing_returned;
mod serde_api;
mod shadow;
mod significant_drop_tightening;
+mod single_call_fn;
mod single_char_lifetime_names;
mod single_component_path_imports;
+mod single_range_in_vec_init;
mod size_of_in_element_count;
mod size_of_ref;
mod slow_vector_initialization;
@@ -299,6 +311,7 @@ mod to_digit_is_some;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
+mod tuple_array_conversions;
mod types;
mod undocumented_unsafe_blocks;
mod unicode;
@@ -326,6 +339,7 @@ mod use_self;
mod useless_conversion;
mod vec;
mod vec_init_then_push;
+mod visibility;
mod wildcard_imports;
mod write;
mod zero_div_zero;
@@ -334,7 +348,7 @@ mod zero_sized_map_values;
pub use crate::utils::conf::{lookup_conf_file, Conf};
use crate::utils::{
- conf::{format_error, metadata::get_configuration_metadata, TryConf},
+ conf::{metadata::get_configuration_metadata, TryConf},
FindAll,
};
@@ -370,23 +384,36 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>
},
};
- let TryConf { conf, errors, warnings } = utils::conf::read(file_name);
+ 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 {
- sess.err(format!(
- "error reading Clippy's configuration file `{}`: {}",
- file_name.display(),
- format_error(error)
- ));
+ 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 {
- sess.struct_warn(format!(
- "error reading Clippy's configuration file `{}`: {}",
- file_name.display(),
- format_error(warning)
- ))
- .emit();
+ 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
@@ -472,26 +499,27 @@ pub(crate) struct LintInfo {
explanation: &'static str,
}
-pub fn explain(name: &str) {
+pub fn explain(name: &str) -> i32 {
let target = format!("clippy::{}", name.to_ascii_uppercase());
- match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
- Some(info) => {
- 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
- 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);
- }
+ 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
+ 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);
}
- },
- None => println!("unknown lint: {name}"),
+ }
+ 0
+ } else {
+ println!("unknown lint: {name}");
+ 1
}
}
@@ -550,6 +578,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default());
store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass));
store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl));
+ store.register_late_pass(|_| {
+ Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new())
+ });
}
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
@@ -658,7 +689,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
});
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
- store.register_late_pass(|_| Box::new(loops::Loops));
+ store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
store.register_late_pass(|_| Box::new(entry::HashMapPass));
@@ -679,7 +710,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
});
let too_large_for_stack = conf.too_large_for_stack;
store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
- store.register_late_pass(move |_| Box::new(vec::UselessVec { too_large_for_stack }));
+ store.register_late_pass(move |_| {
+ Box::new(vec::UselessVec {
+ too_large_for_stack,
+ msrv: msrv(),
+ })
+ });
store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
store.register_late_pass(|_| Box::new(derive::Derive));
@@ -711,7 +747,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let missing_docs_in_crate_items = conf.missing_docs_in_crate_items;
store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
- store.register_late_pass(|_| Box::new(mem_forget::MemForget));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items)));
@@ -737,7 +772,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
- store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
+ store.register_late_pass(|_| Box::<question_mark::QuestionMark>::default());
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
@@ -759,7 +794,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds;
- store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
+ store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv())));
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
@@ -771,7 +806,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
store.register_early_pass(|| Box::new(formatting::Formatting));
store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
- store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
store.register_late_pass(|_| Box::new(returns::Return));
@@ -796,10 +830,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
))
});
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+ let allow_private_module_inception = conf.allow_private_module_inception;
store.register_late_pass(move |_| {
Box::new(enum_variants::EnumVariantNames::new(
enum_variant_name_threshold,
avoid_breaking_exported_api,
+ allow_private_module_inception,
))
});
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
@@ -819,7 +855,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
- store.register_early_pass(|| Box::new(as_conversions::AsConversions));
+ store.register_late_pass(|_| Box::new(as_conversions::AsConversions));
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
let max_fn_params_bools = conf.max_fn_params_bools;
@@ -895,7 +931,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
enable_raw_pointer_heuristic_for_send,
))
});
- store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
+ let accept_comment_above_statement = conf.accept_comment_above_statement;
+ let accept_comment_above_attributes = conf.accept_comment_above_attributes;
+ store.register_late_pass(move |_| {
+ Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(
+ accept_comment_above_statement,
+ accept_comment_above_attributes,
+ ))
+ });
let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
@@ -937,7 +980,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
- store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
@@ -989,9 +1031,49 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
+ let excessive_nesting_threshold = conf.excessive_nesting_threshold;
+ store.register_early_pass(move || {
+ Box::new(excessive_nesting::ExcessiveNesting {
+ excessive_nesting_threshold,
+ nodes: rustc_ast::node_id::NodeSet::new(),
+ })
+ });
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
+ store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
+ store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
+ store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
+ store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
+ store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
+ store.register_late_pass(|_| Box::new(needless_if::NeedlessIf));
+ let allowed_idents_below_min_chars = conf.allowed_idents_below_min_chars.clone();
+ let min_ident_chars_threshold = conf.min_ident_chars_threshold;
+ store.register_late_pass(move |_| {
+ Box::new(min_ident_chars::MinIdentChars {
+ allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(),
+ min_ident_chars_threshold,
+ })
+ });
+ let stack_size_threshold = conf.stack_size_threshold;
+ store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
+ store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
+ store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
+ store.register_late_pass(move |_| {
+ Box::new(single_call_fn::SingleCallFn {
+ avoid_breaking_exported_api,
+ def_id_to_usage: rustc_data_structures::fx::FxHashMap::default(),
+ })
+ });
+ let needless_raw_string_hashes_allow_one = conf.allow_one_hash_in_raw_strings;
+ store.register_early_pass(move || {
+ Box::new(raw_strings::RawStrings {
+ needless_raw_string_hashes_allow_one,
+ })
+ });
+ store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
+ store.register_early_pass(|| Box::new(visibility::Visibility));
+ store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
// 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 986ffcad8..852f67365 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
+use itertools::Itertools;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
@@ -201,7 +202,19 @@ fn check_fn_inner<'tcx>(
span_lint_and_then(
cx,
NEEDLESS_LIFETIMES,
- span.with_hi(sig.decl.output.span().hi()),
+ elidable_lts
+ .iter()
+ .map(|&lt| cx.tcx.def_span(lt))
+ .chain(usages.iter().filter_map(|usage| {
+ if let LifetimeName::Param(def_id) = usage.res
+ && elidable_lts.contains(&def_id)
+ {
+ return Some(usage.ident.span);
+ }
+
+ None
+ }))
+ .collect_vec(),
&format!("the following explicit lifetimes could be elided: {lts}"),
|diag| {
if sig.header.is_async() {
@@ -562,7 +575,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
// if the bounds define new lifetimes, they are fine to occur
let allowed_lts = allowed_lts_from(pred.bound_generic_params);
// now walk the bounds
- for bound in pred.bounds.iter() {
+ for bound in pred.bounds {
walk_param_bound(&mut visitor, bound);
}
// and check that all lifetimes are allowed
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
index 175e2b382..93d6b8086 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
@@ -5,15 +5,76 @@ use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_span::symbol::sym;
+#[derive(Clone, Copy)]
+enum AdjustKind {
+ None,
+ Borrow,
+ BorrowMut,
+ Reborrow,
+ ReborrowMut,
+}
+impl AdjustKind {
+ fn borrow(mutbl: AutoBorrowMutability) -> Self {
+ match mutbl {
+ AutoBorrowMutability::Not => Self::Borrow,
+ AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
+ }
+ }
+
+ fn reborrow(mutbl: AutoBorrowMutability) -> Self {
+ match mutbl {
+ AutoBorrowMutability::Not => Self::Reborrow,
+ AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
+ }
+ }
+
+ fn display(self) -> &'static str {
+ match self {
+ Self::None => "",
+ Self::Borrow => "&",
+ Self::BorrowMut => "&mut ",
+ Self::Reborrow => "&*",
+ Self::ReborrowMut => "&mut *",
+ }
+ }
+}
+
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) {
- let self_ty = cx.typeck_results().expr_ty(self_arg);
- let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
- if !(self_ty == self_ty_adjusted && is_trait_method(cx, call_expr, sym::IntoIterator)) {
+ if !is_trait_method(cx, call_expr, sym::IntoIterator) {
return;
}
+ let typeck = cx.typeck_results();
+ let self_ty = typeck.expr_ty(self_arg);
+ let adjust = match typeck.expr_adjustments(self_arg) {
+ [] => AdjustKind::None,
+ &[
+ Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
+ ..
+ },
+ ] => AdjustKind::borrow(mutbl),
+ &[
+ Adjustment {
+ kind: Adjust::Deref(_), ..
+ },
+ Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
+ target,
+ },
+ ] => {
+ if self_ty == target && matches!(mutbl, AutoBorrowMutability::Not) {
+ AdjustKind::None
+ } else {
+ AdjustKind::reborrow(mutbl)
+ }
+ },
+ _ => return,
+ };
+
let mut applicability = Applicability::MachineApplicable;
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
span_lint_and_sugg(
@@ -23,7 +84,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<
"it is more concise to loop over containers instead of using explicit \
iteration methods",
"to write this more concisely, try",
- object.to_string(),
+ format!("{}{object}", adjust.display()),
applicability,
);
}
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 151c7f1d5..5c5a4cfce 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,75 +1,235 @@
use super::EXPLICIT_ITER_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{
+ implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
+ make_normalized_projection_with_regions, normalize_with_regions,
+};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut};
use rustc_span::sym;
-pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
- let should_lint = match method_name {
- "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
- "into_iter" if is_trait_method(cx, arg, sym::IntoIterator) => {
- let receiver_ty = cx.typeck_results().expr_ty(self_arg);
- let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
- let ref_receiver_ty = cx.tcx.mk_ref(
- cx.tcx.lifetimes.re_erased,
- ty::TypeAndMut {
- ty: receiver_ty,
- mutbl: Mutability::Not,
- },
- );
- receiver_ty_adjusted == ref_receiver_ty
- },
- _ => false,
- };
-
- if !should_lint {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) {
+ let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else {
return;
+ };
+ if let ty::Array(_, count) = *ty.peel_refs().kind() {
+ if !ty.is_ref() {
+ if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
+ return;
+ }
+ } else if count
+ .try_eval_target_usize(cx.tcx, cx.param_env)
+ .map_or(true, |x| x > 32)
+ && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
+ {
+ return;
+ }
}
let mut applicability = Applicability::MachineApplicable;
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
- let muta = if method_name == "iter_mut" { "mut " } else { "" };
span_lint_and_sugg(
cx,
EXPLICIT_ITER_LOOP,
- arg.span,
+ call_expr.span,
"it is more concise to loop over references to containers instead of using explicit \
iteration methods",
"to write this more concisely, try",
- format!("&{muta}{object}"),
+ format!("{}{object}", adjust.display()),
applicability,
);
}
-/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
-/// for `&T` and `&mut T`, such as `Vec`.
-#[rustfmt::skip]
-fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- // no walk_ptrs_ty: calling iter() on a reference can make sense because it
- // will allow further borrows afterwards
- let ty = cx.typeck_results().expr_ty(e);
- is_iterable_array(ty, cx) ||
- is_type_diagnostic_item(cx, ty, sym::Vec) ||
- is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
- is_type_diagnostic_item(cx, ty, sym::HashMap) ||
- is_type_diagnostic_item(cx, ty, sym::HashSet) ||
- is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
- is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
- is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
- is_type_diagnostic_item(cx, ty, sym::BTreeSet)
+#[derive(Clone, Copy)]
+enum AdjustKind {
+ None,
+ Borrow,
+ BorrowMut,
+ Deref,
+ Reborrow,
+ ReborrowMut,
}
+impl AdjustKind {
+ fn borrow(mutbl: Mutability) -> Self {
+ match mutbl {
+ Mutability::Not => Self::Borrow,
+ Mutability::Mut => Self::BorrowMut,
+ }
+ }
-fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
- // IntoIterator is currently only implemented for array sizes <= 32 in rustc
- match ty.kind() {
- ty::Array(_, n) => n
- .try_eval_target_usize(cx.tcx, cx.param_env)
- .map_or(false, |val| (0..=32).contains(&val)),
- _ => false,
+ fn auto_borrow(mutbl: AutoBorrowMutability) -> Self {
+ match mutbl {
+ AutoBorrowMutability::Not => Self::Borrow,
+ AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
+ }
+ }
+
+ fn reborrow(mutbl: Mutability) -> Self {
+ match mutbl {
+ Mutability::Not => Self::Reborrow,
+ Mutability::Mut => Self::ReborrowMut,
+ }
+ }
+
+ fn auto_reborrow(mutbl: AutoBorrowMutability) -> Self {
+ match mutbl {
+ AutoBorrowMutability::Not => Self::Reborrow,
+ AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
+ }
+ }
+
+ fn display(self) -> &'static str {
+ match self {
+ Self::None => "",
+ Self::Borrow => "&",
+ Self::BorrowMut => "&mut ",
+ Self::Deref => "*",
+ Self::Reborrow => "&*",
+ Self::ReborrowMut => "&mut *",
+ }
+ }
+}
+
+/// Checks if an `iter` or `iter_mut` call returns `IntoIterator::IntoIter`. Returns how the
+/// argument needs to be adjusted.
+#[expect(clippy::too_many_lines)]
+fn is_ref_iterable<'tcx>(
+ cx: &LateContext<'tcx>,
+ self_arg: &Expr<'_>,
+ call_expr: &Expr<'_>,
+) -> Option<(AdjustKind, Ty<'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 &[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, [])
+ && let Some(into_iter_ty) =
+ make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
+ && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
+ && into_iter_ty == req_res_ty
+ {
+ let adjustments = typeck.expr_adjustments(self_arg);
+ let self_ty = typeck.expr_ty(self_arg);
+ let self_is_copy = is_copy(cx, self_ty);
+
+ if adjustments.is_empty() && self_is_copy {
+ // Exact type match, already checked earlier
+ return Some((AdjustKind::None, self_ty));
+ }
+
+ let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
+ .subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
+ let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
+ Some(mutbl)
+ } else {
+ None
+ };
+
+ if !adjustments.is_empty() {
+ if self_is_copy {
+ // Using by value won't consume anything
+ 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
+ {
+ return Some((AdjustKind::None, self_ty));
+ }
+ } else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
+ && let Some(mutbl) = mutbl
+ {
+ // Attempt to reborrow the mutable reference
+ let self_ty = if mutbl.is_mut() {
+ self_ty
+ } else {
+ Ty::new_ref(cx.tcx,region, TypeAndMut { 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
+ {
+ return Some((AdjustKind::reborrow(mutbl), self_ty));
+ }
+ }
+ }
+ if let Some(mutbl) = mutbl
+ && !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,
+ });
+ 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
+ {
+ return Some((AdjustKind::borrow(mutbl), self_ty));
+ }
+ }
+
+ match adjustments {
+ [] => Some((AdjustKind::None, self_ty)),
+ &[
+ Adjustment { kind: Adjust::Deref(_), ..},
+ Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
+ target,
+ },
+ ..
+ ] => {
+ if target != self_ty
+ && implements_trait(cx, target, trait_id, &[])
+ && let Some(ty) =
+ make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+ && ty == res_ty
+ {
+ Some((AdjustKind::auto_reborrow(mutbl), target))
+ } else {
+ None
+ }
+ }
+ &[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
+ if is_copy(cx, target)
+ && implements_trait(cx, target, trait_id, &[])
+ && let Some(ty) =
+ make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+ && ty == res_ty
+ {
+ Some((AdjustKind::Deref, target))
+ } else {
+ None
+ }
+ }
+ &[
+ Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
+ target,
+ },
+ ..
+ ] => {
+ if self_ty.is_ref()
+ && implements_trait(cx, target, trait_id, &[])
+ && let Some(ty) =
+ make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+ && ty == res_ty
+ {
+ Some((AdjustKind::auto_borrow(mutbl), target))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ } else {
+ None
}
}
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index d4c3f76b8..7d1f8ef29 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -51,7 +51,7 @@ pub(super) fn check<'tcx>(
iter_b = Some(get_assignment(body));
}
- let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter());
+ let assignments = iter_a.into_iter().flatten().chain(iter_b);
let big_sugg = assignments
// The only statements in the for loops can be indexed assignments from
@@ -402,7 +402,7 @@ fn get_assignments<'a, 'tcx>(
StmtKind::Local(..) | StmtKind::Item(..) => None,
StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
})
- .chain((*expr).into_iter())
+ .chain(*expr)
.filter(move |e| {
if let ExprKind::AssignOp(_, place, _) = e.kind {
path_to_local(place).map_or(false, |id| {
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index f83ad388a..529189b52 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -20,9 +20,10 @@ mod while_let_loop;
mod while_let_on_iterator;
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_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
@@ -479,7 +480,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Check for unnecessary `if let` usage in a for loop
+ /// Checks for unnecessary `if let` usage in a for loop
/// where only the `Some` or `Ok` variant of the iterator element is used.
///
/// ### Why is this bad?
@@ -511,7 +512,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Check for empty spin loops
+ /// Checks for empty spin loops
///
/// ### Why is this bad?
/// The loop body should have something like `thread::park()` or at least
@@ -547,7 +548,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Check for manual implementations of Iterator::find
+ /// Checks for manual implementations of Iterator::find
///
/// ### Why is this bad?
/// It doesn't affect performance, but using `find` is shorter and easier to read.
@@ -606,7 +607,15 @@ declare_clippy_lint! {
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
}
-declare_lint_pass!(Loops => [
+pub struct Loops {
+ msrv: Msrv,
+}
+impl Loops {
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+impl_lint_pass!(Loops => [
MANUAL_MEMCPY,
MANUAL_FLATTEN,
NEEDLESS_RANGE_LOOP,
@@ -645,7 +654,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
if body.span.from_expansion() {
return;
}
- check_for_loop(cx, pat, arg, body, expr, span);
+ self.check_for_loop(cx, pat, arg, body, expr, span);
if let ExprKind::Block(block, _) = body.kind {
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
}
@@ -678,46 +687,48 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
manual_while_let_some::check(cx, condition, body, span);
}
}
+
+ extract_msrv_attr!(LateContext);
}
-fn check_for_loop<'tcx>(
- cx: &LateContext<'tcx>,
- pat: &'tcx Pat<'_>,
- arg: &'tcx Expr<'_>,
- body: &'tcx Expr<'_>,
- expr: &'tcx Expr<'_>,
- span: Span,
-) {
- let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
- if !is_manual_memcpy_triggered {
- needless_range_loop::check(cx, pat, arg, body, expr);
- explicit_counter_loop::check(cx, pat, arg, body, expr);
+impl Loops {
+ fn check_for_loop<'tcx>(
+ &self,
+ cx: &LateContext<'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+ span: Span,
+ ) {
+ let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
+ if !is_manual_memcpy_triggered {
+ needless_range_loop::check(cx, pat, arg, body, expr);
+ explicit_counter_loop::check(cx, pat, arg, body, expr);
+ }
+ self.check_for_loop_arg(cx, pat, arg);
+ for_kv_map::check(cx, pat, arg, body);
+ mut_range_bound::check(cx, arg, body);
+ single_element_loop::check(cx, pat, arg, body, expr);
+ 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);
}
- check_for_loop_arg(cx, pat, arg);
- for_kv_map::check(cx, pat, arg, body);
- mut_range_bound::check(cx, arg, body);
- single_element_loop::check(cx, pat, arg, body, expr);
- 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);
-}
-fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
- if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
- let method_name = method.ident.as_str();
- // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
- match method_name {
- "iter" | "iter_mut" => {
- explicit_iter_loop::check(cx, self_arg, arg, method_name);
- },
- "into_iter" => {
- explicit_iter_loop::check(cx, self_arg, arg, method_name);
- explicit_into_iter_loop::check(cx, self_arg, arg);
- },
- "next" => {
- iter_next_loop::check(cx, arg);
- },
- _ => {},
+ fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
+ if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
+ match method.ident.as_str() {
+ "iter" | "iter_mut" => {
+ explicit_iter_loop::check(cx, self_arg, arg, &self.msrv);
+ },
+ "into_iter" => {
+ explicit_into_iter_loop::check(cx, self_arg, arg);
+ },
+ "next" => {
+ iter_next_loop::check(cx, arg);
+ },
+ _ => {},
+ }
}
}
}
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 5f1fdf00b..ee338c6be 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -1,22 +1,23 @@
use super::utils::make_iterator_snippet;
use super::NEVER_LOOP;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::consts::constant;
use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet;
+use clippy_utils::{consts::Constant, diagnostics::span_lint_and_then};
use rustc_errors::Applicability;
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::Span;
use std::iter::{once, Iterator};
-pub(super) fn check(
- cx: &LateContext<'_>,
- block: &Block<'_>,
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ block: &Block<'tcx>,
loop_id: HirId,
span: Span,
for_loop: Option<&ForLoop<'_>>,
) {
- match never_loop_block(block, &mut Vec::new(), loop_id) {
+ match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
NeverLoopResult::AlwaysBreak => {
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
if let Some(ForLoop {
@@ -95,7 +96,12 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirI
}
}
-fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
+fn never_loop_block<'tcx>(
+ cx: &LateContext<'tcx>,
+ block: &Block<'tcx>,
+ ignore_ids: &mut Vec<HirId>,
+ main_loop_id: HirId,
+) -> NeverLoopResult {
let iter = block
.stmts
.iter()
@@ -103,10 +109,10 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id
.chain(block.expr.map(|expr| (expr, None)));
iter.map(|(e, els)| {
- let e = never_loop_expr(e, ignore_ids, main_loop_id);
+ let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
// els is an else block in a let...else binding
els.map_or(e, |els| {
- combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids)
+ combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids)
})
})
.fold(NeverLoopResult::Otherwise, combine_seq)
@@ -122,7 +128,12 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
}
#[allow(clippy::too_many_lines)]
-fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
+fn never_loop_expr<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'tcx>,
+ ignore_ids: &mut Vec<HirId>,
+ main_loop_id: HirId,
+) -> NeverLoopResult {
match expr.kind {
ExprKind::Unary(_, e)
| ExprKind::Cast(e, _)
@@ -130,45 +141,51 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
| ExprKind::Field(e, _)
| ExprKind::AddrOf(_, _, e)
| ExprKind::Repeat(e, _)
- | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
- ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
- ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
+ | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id),
+ ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id),
+ ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id),
ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
+ cx,
&mut std::iter::once(receiver).chain(es.iter()),
ignore_ids,
main_loop_id,
),
ExprKind::Struct(_, fields, base) => {
- let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
+ let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
if let Some(base) = base {
- combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id))
+ combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id))
} else {
fields
}
},
- ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
+ ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _)
| ExprKind::AssignOp(_, e1, e2)
- | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
+ | ExprKind::Index(e1, e2) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
ExprKind::Loop(b, _, _, _) => {
// Break can come from the inner loop so remove them.
- absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
+ absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
},
ExprKind::If(e, e2, e3) => {
- let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
- let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
+ let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id);
+ let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id);
+ // If we know the `if` condition evaluates to `true`, don't check everything past it; it
+ // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
+ if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) {
+ return combine_seq(e1, e2);
+ }
let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
- never_loop_expr(e, ignore_ids, main_loop_id)
+ never_loop_expr(cx, e, ignore_ids, main_loop_id)
});
combine_seq(e1, combine_branches(e2, e3, ignore_ids))
},
ExprKind::Match(e, arms, _) => {
- let e = never_loop_expr(e, ignore_ids, main_loop_id);
+ let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
if arms.is_empty() {
e
} else {
- let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
+ let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
combine_seq(e, arms)
}
},
@@ -176,7 +193,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
if l.is_some() {
ignore_ids.push(b.hir_id);
}
- let ret = never_loop_block(b, ignore_ids, main_loop_id);
+ let ret = never_loop_block(cx, b, ignore_ids, main_loop_id);
if l.is_some() {
ignore_ids.pop();
}
@@ -198,25 +215,30 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
// checks if break targets a block instead of a loop
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
.map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
- never_loop_expr(e, ignore_ids, main_loop_id)
+ never_loop_expr(cx, e, ignore_ids, main_loop_id)
}),
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
combine_seq(
- never_loop_expr(e, ignore_ids, main_loop_id),
+ never_loop_expr(cx, e, ignore_ids, main_loop_id),
NeverLoopResult::AlwaysBreak,
)
}),
+ ExprKind::Become(e) => combine_seq(
+ never_loop_expr(cx, e, ignore_ids, main_loop_id),
+ NeverLoopResult::AlwaysBreak,
+ ),
ExprKind::InlineAsm(asm) => asm
.operands
.iter()
.map(|(o, _)| match o {
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
- never_loop_expr(expr, ignore_ids, main_loop_id)
+ never_loop_expr(cx, expr, ignore_ids, main_loop_id)
},
InlineAsmOperand::Out { expr, .. } => {
- never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
+ never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id)
},
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
+ cx,
&mut once(*in_expr).chain(out_expr.iter().copied()),
ignore_ids,
main_loop_id,
@@ -236,22 +258,24 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
}
}
-fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
+fn never_loop_expr_all<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
+ cx: &LateContext<'tcx>,
es: &mut T,
ignore_ids: &mut Vec<HirId>,
main_loop_id: HirId,
) -> NeverLoopResult {
- es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
+ es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id))
.fold(NeverLoopResult::Otherwise, combine_seq)
}
-fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
+fn never_loop_expr_branch<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
+ cx: &LateContext<'tcx>,
e: &mut T,
ignore_ids: &mut Vec<HirId>,
main_loop_id: HirId,
) -> NeverLoopResult {
e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
- combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids)
+ combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids)
})
}
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 9d9341559..f7b3b2358 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
@@ -148,7 +148,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
}
fn visit_block(&mut self, b: &'tcx Block<'_>) {
- for stmt in b.stmts.iter() {
+ for stmt in b.stmts {
self.visit_stmt(stmt);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index e2e6a87a3..8e322a979 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
});
if !id.is_local();
then {
- for kid in cx.tcx.module_children(id).iter() {
+ for kid in cx.tcx.module_children(id) {
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
let span = mac_attr.span;
let def_path = cx.tcx.def_path_str(mac_id);
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 3f8b42ffe..59e421c16 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -6,17 +6,18 @@ use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{Descend, Visitable};
use if_chain::if_chain;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
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::sym;
+use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use serde::Deserialize;
use std::ops::ControlFlow;
+use std::slice;
declare_clippy_lint! {
/// ### What it does
@@ -77,53 +78,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
local.els.is_none() &&
local.ty.is_none() &&
init.span.ctxt() == stmt.span.ctxt() &&
- 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_chain! {
- if expr_is_simple_identity(let_pat, if_then);
- if let Some(if_else) = if_else;
- if expr_diverges(cx, if_else);
- then {
- emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else);
- }
- },
- IfLetOrMatch::Match(match_expr, arms, source) => {
- if self.matches_behaviour == MatchLintBehaviour::Never {
- return;
- }
- if source != MatchSource::Normal {
- return;
- }
- // Any other number than two arms doesn't (necessarily)
- // have a trivial mapping to let else.
- if arms.len() != 2 {
- return;
- }
- // Guards don't give us an easy mapping either
- if arms.iter().any(|arm| arm.guard.is_some()) {
- return;
- }
- let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
- let diverging_arm_opt = arms
- .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; };
- // 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.
- // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
- if idx == 0 {
- return;
- }
- let pat_arm = &arms[1 - idx];
- if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
- return;
- }
+ 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_chain! {
+ if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then);
+ if let Some(if_else) = if_else;
+ if expr_diverges(cx, if_else);
+ then {
+ emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
+ }
+ },
+ IfLetOrMatch::Match(match_expr, arms, source) => {
+ if self.matches_behaviour == MatchLintBehaviour::Never {
+ return;
+ }
+ if source != MatchSource::Normal {
+ return;
+ }
+ // Any other number than two arms doesn't (necessarily)
+ // have a trivial mapping to let else.
+ if arms.len() != 2 {
+ return;
+ }
+ // Guards don't give us an easy mapping either
+ if arms.iter().any(|arm| arm.guard.is_some()) {
+ return;
+ }
+ let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
+ let diverging_arm_opt = arms
+ .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; };
+ // 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.
+ // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
+ if idx == 0 {
+ return;
+ }
+ let pat_arm = &arms[1 - idx];
+ let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
+ return
+ };
- emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body);
- },
- }
+ emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
+ },
+ }
};
}
@@ -134,7 +136,7 @@ fn emit_manual_let_else(
cx: &LateContext<'_>,
span: Span,
expr: &Expr<'_>,
- local: &Pat<'_>,
+ ident_map: &FxHashMap<Symbol, &Pat<'_>>,
pat: &Pat<'_>,
else_body: &Expr<'_>,
) {
@@ -145,10 +147,9 @@ fn emit_manual_let_else(
"this could be rewritten as `let...else`",
|diag| {
// This is far from perfect, for example there needs to be:
- // * mut additions for the bindings
- // * renamings of the bindings for `PatKind::Or`
+ // * renamings of the bindings for many `PatKind`s like slices, etc.
+ // * limitations in the existing replacement algorithms
// * unused binding collision detection with existing ones
- // * putting patterns with at the top level | inside ()
// for this to be machine applicable.
let mut app = Applicability::HasPlaceholders;
let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
@@ -159,28 +160,131 @@ fn emit_manual_let_else(
} else {
format!("{{ {sn_else} }}")
};
- let sn_bl = match pat.kind {
- PatKind::Or(..) => {
- let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
- format!("({sn_pat})")
- },
- // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
- PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => {
- let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
- let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app);
- format!("{sn_wrapper}({sn_inner})")
- },
- _ => {
- let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
- sn_pat.into_owned()
- },
- };
+ let sn_bl = replace_in_pattern(cx, span, ident_map, pat, &mut app, true);
let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};");
diag.span_suggestion(span, "consider writing", sugg, app);
},
);
}
+/// Replaces the locals in the pattern
+///
+/// For this example:
+///
+/// ```ignore
+/// let (a, FooBar { b, c }) = if let Bar { Some(a_i), b_i } = ex { (a_i, b_i) } else { return };
+/// ```
+///
+/// We have:
+///
+/// ```ignore
+/// pat: Bar { Some(a_i), b_i }
+/// ident_map: (a_i) -> (a), (b_i) -> (FooBar { b, c })
+/// ```
+///
+/// We return:
+///
+/// ```ignore
+/// Bar { Some(a), b_i: FooBar { b, c } }
+/// ```
+fn replace_in_pattern(
+ cx: &LateContext<'_>,
+ span: Span,
+ ident_map: &FxHashMap<Symbol, &Pat<'_>>,
+ pat: &Pat<'_>,
+ app: &mut Applicability,
+ top_level: bool,
+) -> String {
+ // We put a labeled block here so that we can implement the fallback in this function.
+ // As the function has multiple call sites, implementing the fallback via an Option<T>
+ // return type and unwrap_or_else would cause repetition. Similarly, the function also
+ // invokes the fall back multiple times.
+ 'a: {
+ // If the ident map is empty, there is no replacement to do.
+ // The code following this if assumes a non-empty ident_map.
+ if ident_map.is_empty() {
+ break 'a;
+ }
+
+ match pat.kind {
+ PatKind::Binding(_ann, _id, binding_name, opt_subpt) => {
+ let Some(pat_to_put) = ident_map.get(&binding_name.name) else { break 'a };
+ let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
+ if let Some(subpt) = opt_subpt {
+ let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
+ return format!("{sn_ptp} @ {subpt}");
+ }
+ return sn_ptp.to_string();
+ },
+ PatKind::Or(pats) => {
+ let patterns = pats
+ .iter()
+ .map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
+ .collect::<Vec<_>>();
+ let or_pat = patterns.join(" | ");
+ if top_level {
+ return format!("({or_pat})");
+ }
+ return or_pat;
+ },
+ PatKind::Struct(path, fields, has_dot_dot) => {
+ 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)
+ {
+ 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);
+ // TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is
+ // a PatKind::Binding but that is also hard to get right.
+ if sn_fld_name == sn_ptp {
+ // Field init shorthand
+ return format!("{sn_fld_name}");
+ }
+ return format!("{sn_fld_name}: {sn_ptp}");
+ }
+ let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app);
+ sn_fld.into_owned()
+ })
+ .collect::<Vec<_>>();
+ let fields_string = fields.join(", ");
+
+ let dot_dot_str = if has_dot_dot { " .." } else { "" };
+ let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app);
+ return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}");
+ },
+ // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
+ PatKind::TupleStruct(ref w, args, dot_dot_pos) => {
+ let mut args = args
+ .iter()
+ .map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
+ .collect::<Vec<_>>();
+ if let Some(pos) = dot_dot_pos.as_opt_usize() {
+ args.insert(pos, "..".to_owned());
+ }
+ let args = args.join(", ");
+ let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
+ return format!("{sn_wrapper}({args})");
+ },
+ PatKind::Tuple(args, dot_dot_pos) => {
+ let mut args = args
+ .iter()
+ .map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
+ .collect::<Vec<_>>();
+ if let Some(pos) = dot_dot_pos.as_opt_usize() {
+ args.insert(pos, "..".to_owned());
+ }
+ let args = args.join(", ");
+ return format!("({args})");
+ },
+ _ => {},
+ }
+ }
+ let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app);
+ sn_pat.into_owned()
+}
+
/// Check whether an expression is divergent. May give false negatives.
fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
struct V<'cx, 'tcx> {
@@ -319,37 +423,74 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo
!has_disallowed
}
-/// Checks if the passed block is a simple identity referring to bindings created by the pattern
-fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool {
- // We support patterns with multiple bindings and tuples, like:
- // let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
+/// Checks if the passed block is a simple identity referring to bindings created by the pattern,
+/// and if yes, returns a mapping between the relevant sub-pattern and the identifier it corresponds
+/// to.
+///
+/// We support patterns with multiple bindings and tuples, e.g.:
+///
+/// ```ignore
+/// let (foo_o, bar_o) = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
+/// ```
+///
+/// The expected params would be:
+///
+/// ```ignore
+/// local_pat: (foo_o, bar_o)
+/// let_pat: (Some(foo), bar)
+/// expr: (foo, bar)
+/// ```
+///
+/// We build internal `sub_pats` so that it looks like `[foo_o, bar_o]` and `paths` so that it looks
+/// like `[foo, bar]`. Then we turn that into `FxHashMap [(foo) -> (foo_o), (bar) -> (bar_o)]` which
+/// we return.
+fn expr_simple_identity_map<'a, 'hir>(
+ local_pat: &'a Pat<'hir>,
+ let_pat: &'_ Pat<'hir>,
+ expr: &'_ Expr<'hir>,
+) -> Option<FxHashMap<Symbol, &'a Pat<'hir>>> {
let peeled = peel_blocks(expr);
- let paths = match peeled.kind {
- ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs,
- ExprKind::Path(_) => std::slice::from_ref(peeled),
- _ => return false,
+ let (sub_pats, paths) = match (local_pat.kind, peeled.kind) {
+ (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => {
+ (pats, exprs)
+ },
+ (_, ExprKind::Path(_)) => (slice::from_ref(local_pat), slice::from_ref(peeled)),
+ _ => return None,
};
+
+ // There is some length mismatch, which indicates usage of .. in the patterns above e.g.:
+ // let (a, ..) = if let [a, b, _c] = ex { (a, b) } else { ... };
+ // We bail in these cases as they should be rare.
+ if paths.len() != sub_pats.len() {
+ return None;
+ }
+
let mut pat_bindings = FxHashSet::default();
- pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
+ let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
pat_bindings.insert(ident);
});
if pat_bindings.len() < paths.len() {
- return false;
+ // This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple
+ // times. We don't support these cases so we bail here. E.g.:
+ // let foo = 0;
+ // let (new_foo, bar, bar_copied) = if let Some(bar) = Some(0) { (foo, bar, bar) } else { .. };
+ return None;
}
- for path in paths {
- if_chain! {
- if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind;
- if let [path_seg] = path.segments;
- then {
- if !pat_bindings.remove(&path_seg.ident) {
- return false;
- }
- } else {
- return false;
+ 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
+ {
+ let ident = path_seg.ident;
+ if !pat_bindings.remove(&ident) {
+ return None;
}
+ ident_map.insert(ident.name, sub_pat);
+ } else {
+ return None;
}
}
- true
+ Some(ident_map)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
new file mode 100644
index 000000000..65ff55520
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -0,0 +1,123 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::LitKind;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_hir::ExprKind;
+use rustc_hir::PatKind;
+use rustc_hir::RangeEnd;
+use rustc_lint::LintContext;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Looks for combined OR patterns that are all contained in a specific range,
+ /// e.g. `6 | 4 | 5 | 9 | 7 | 8` can be rewritten as `4..=9`.
+ ///
+ /// ### Why is this bad?
+ /// Using an explicit range is more concise and easier to read.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = 6;
+ /// let foo = matches!(x, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = 6;
+ /// let foo = matches!(x, 1..=10);
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub MANUAL_RANGE_PATTERNS,
+ complexity,
+ "manually writing range patterns using a combined OR pattern (`|`)"
+}
+declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
+
+fn expr_as_u128(expr: &Expr<'_>) -> Option<u128> {
+ if let ExprKind::Lit(lit) = expr.kind
+ && let LitKind::Int(num, _) = lit.node
+ {
+ Some(num)
+ } else {
+ None
+ }
+}
+
+impl LateLintPass<'_> for ManualRangePatterns {
+ fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
+ if in_external_macro(cx.sess(), pat.span) {
+ return;
+ }
+
+ // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
+ if let PatKind::Or(pats) = pat.kind
+ && pats.len() >= 3
+ {
+ let mut min = u128::MAX;
+ let mut max = 0;
+ let mut numbers_found = FxHashSet::default();
+ let mut ranges_found = Vec::new();
+
+ for pat in pats {
+ if let PatKind::Lit(lit) = pat.kind
+ && let Some(num) = expr_as_u128(lit)
+ {
+ numbers_found.insert(num);
+
+ min = min.min(num);
+ max = max.max(num);
+ } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind
+ && let Some(left) = expr_as_u128(left)
+ && let Some(right) = expr_as_u128(right)
+ && right >= left
+ {
+ min = min.min(left);
+ max = max.max(right);
+ ranges_found.push(left..=match end {
+ RangeEnd::Included => right,
+ RangeEnd::Excluded => right - 1,
+ });
+ } else {
+ return;
+ }
+ }
+
+ let contains_whole_range = 'contains: {
+ let mut num = min;
+ while num <= max {
+ if numbers_found.contains(&num) {
+ num += 1;
+ }
+ // Given a list of (potentially overlapping) ranges like:
+ // 1..=5, 3..=7, 6..=10
+ // We want to find the range with the highest end that still contains the current number
+ else if let Some(range) = ranges_found
+ .iter()
+ .filter(|range| range.contains(&num))
+ .max_by_key(|range| range.end())
+ {
+ num = range.end() + 1;
+ } else {
+ break 'contains false;
+ }
+ }
+ break 'contains true;
+ };
+
+ if contains_whole_range {
+ span_lint_and_sugg(
+ cx,
+ MANUAL_RANGE_PATTERNS,
+ pat.span,
+ "this OR pattern can be rewritten using a range",
+ "try",
+ format!("{min}..={max}"),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index ae8262ace..3d2fbea63 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
-use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
use core::cmp::Ordering;
use core::iter;
use core::slice;
@@ -9,6 +9,7 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
+use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Symbol;
@@ -103,17 +104,21 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
if matches!(arm2.pat.kind, PatKind::Wild) {
- span_lint_and_then(
- cx,
- MATCH_SAME_ARMS,
- arm1.span,
- "this match arm has an identical body to the `_` wildcard arm",
- |diag| {
- diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
- .help("or try changing either arm body")
- .span_note(arm2.span, "`_` wildcard arm here");
- },
- );
+ if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
+ || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
+ {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ arm1.span,
+ "this match arm has an identical body to the `_` wildcard arm",
+ |diag| {
+ diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
+ .help("or try changing either arm body")
+ .span_note(arm2.span, "`_` wildcard arm here");
+ },
+ );
+ }
} else {
let back_block = backwards_blocking_idxs[j];
let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
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 42f1e2629..de911f7a0 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
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
-use clippy_utils::{is_wild, peel_blocks_with_stmt};
+use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt};
use rustc_hir::{Arm, Expr, PatKind};
use rustc_lint::LateContext;
use rustc_span::symbol::{kw, sym};
@@ -10,6 +10,11 @@ use rustc_span::symbol::{kw, sym};
use super::MATCH_WILD_ERR_ARM;
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
+ // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now
+ if in_constant(cx, ex.hir_id) {
+ return;
+ }
+
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
for arm in arms {
@@ -20,7 +25,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
let mut ident_bind_name = kw::Underscore;
if !matching_wild {
// Looking for unused bindings (i.e.: `_e`)
- for pat in inner.iter() {
+ for pat in inner {
if let PatKind::Binding(_, id, ident, None) = pat.kind {
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
ident_bind_name = ident.name;
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 55ec9d447..00fa3eb9b 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -25,7 +25,7 @@ mod wild_in_or_pats;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_opt, walk_span_to_context};
-use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text};
+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};
use rustc_lexer::TokenKind;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -38,6 +38,11 @@ declare_clippy_lint! {
/// Checks for matches with a single arm where an `if let`
/// will usually suffice.
///
+ /// This intentionally does not lint if there are comments
+ /// inside of the other arm, so as to allow the user to document
+ /// why having another explicit pattern with an empty body is necessary,
+ /// or because the comments need to be preserved for other reasons.
+ ///
/// ### Why is this bad?
/// Just readability – `if let` nests less than a `match`.
///
@@ -559,6 +564,9 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `match` with identical arm bodies.
///
+ /// Note: Does not lint on wildcards if the `non_exhaustive_omitted_patterns_lint` feature is
+ /// enabled and disallowed.
+ ///
/// ### Why is this bad?
/// This is probably a copy & paste error. If arm bodies
/// are the same on purpose, you can factor them
@@ -777,7 +785,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Check for temporaries returned from function calls in a match scrutinee that have the
+ /// Checks for temporaries returned from function calls in a match scrutinee that have the
/// `clippy::has_significant_drop` attribute.
///
/// ### Why is this bad?
@@ -974,12 +982,16 @@ impl_lint_pass!(Matches => [
impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_external_macro(cx.sess(), expr.span) {
+ if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) {
return;
}
let from_expansion = expr.span.from_expansion();
if let ExprKind::Match(ex, arms, source) = expr.kind {
+ if is_direct_expn_of(expr.span, "matches").is_some() {
+ redundant_pattern_match::check_match(cx, expr, ex, arms);
+ }
+
if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
return;
}
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 abf2525a6..8be3c178a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
@@ -41,7 +41,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())),
ty,
);
- miri_to_const(cx.tcx, min_constant)?
+ miri_to_const(cx, min_constant)?
},
};
let rhs_const = match rhs {
@@ -52,7 +52,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())),
ty,
);
- miri_to_const(cx.tcx, max_constant)?
+ miri_to_const(cx, max_constant)?
},
};
let lhs_val = lhs_const.int_value(cx, ty)?;
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index e81e09da4..479cfd835 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -1,10 +1,10 @@
use super::REDUNDANT_PATTERN_MATCHING;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
-use clippy_utils::{higher, is_trait_method};
+use clippy_utils::{higher, is_expn_of, is_trait_method};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@@ -190,24 +190,19 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
if let Some(good_method) = found_good_method(cx, arms, node_pair) {
- let span = expr.span.to(op.span);
+ let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span));
let result_expr = match &op.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op,
};
- span_lint_and_then(
+ span_lint_and_sugg(
cx,
REDUNDANT_PATTERN_MATCHING,
- expr.span,
+ span,
&format!("redundant pattern matching, consider using `{good_method}`"),
- |diag| {
- diag.span_suggestion(
- span,
- "try this",
- format!("{}.{good_method}", snippet(cx, result_expr.span, "_")),
- Applicability::MaybeIncorrect, // snippet
- );
- },
+ "try this",
+ format!("{}.{good_method}", snippet(cx, result_expr.span, "_")),
+ Applicability::MachineApplicable,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 794527539..37528d9f7 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -140,7 +140,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
}
}
- for generic_arg in b.iter() {
+ for generic_arg in *b {
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
if self.has_sig_drop_attr(cx, ty) {
return true;
@@ -329,6 +329,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
ExprKind::Field(..) |
ExprKind::Index(..) |
ExprKind::Ret(..) |
+ ExprKind::Become(..) |
ExprKind::Repeat(..) |
ExprKind::Yield(..) => walk_expr(self, ex),
ExprKind::AddrOf(_, _, _) |
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 ad47c1389..35627d6c6 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{expr_block, snippet};
+use clippy_utils::source::{expr_block, get_source_text, snippet};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
use core::cmp::max;
@@ -7,10 +7,26 @@ use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
+/// Checks if there are comments contained within a span.
+/// This is a very "naive" check, as it just looks for the literal characters // and /* in the
+/// source text. This won't be accurate if there are potentially expressions contained within the
+/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
+/// match arms.
+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"/*")
+ } else {
+ false
+ }
+}
+
#[rustfmt::skip]
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
@@ -25,7 +41,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
return;
}
let els = arms[1].body;
- let els = if is_unit_expr(peel_blocks(els)) {
+ let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
None
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
@@ -35,7 +51,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, don't lint
+ // not a block or an emtpy block w/ comments, don't lint
return;
};
diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
index 704c34c32..3a7f1e034 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -81,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
/// Finds function return type by examining return expressions in match arms.
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
- for arm in arms.iter() {
+ for arm in *arms {
if let ExprKind::Ret(Some(ret)) = arm.body.kind {
return Some(cx.typeck_results().expr_ty(ret));
}
diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs
deleted file mode 100644
index d6c235b5a..000000000
--- a/src/tools/clippy/clippy_lints/src/mem_forget.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-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
- /// Checks for usage of `std::mem::forget(t)` where `t` is
- /// `Drop`.
- ///
- /// ### Why is this bad?
- /// `std::mem::forget(t)` prevents `t` from running its
- /// destructor, possibly causing leaks.
- ///
- /// ### Example
- /// ```rust
- /// # use std::mem;
- /// # use std::rc::Rc;
- /// mem::forget(Rc::new(55))
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MEM_FORGET,
- restriction,
- "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
-}
-
-declare_lint_pass!(MemForget => [MEM_FORGET]);
-
-impl<'tcx> LateLintPass<'tcx> for MemForget {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind {
- if let ExprKind::Path(ref qpath) = path_expr.kind {
- if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
- if cx.tcx.is_diagnostic_item(sym::mem_forget, def_id) {
- let forgot_ty = cx.typeck_results().expr_ty(first_arg);
-
- if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
- span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
- }
- }
- }
- }
- }
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
new file mode 100644
index 000000000..d0c79dc11
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
@@ -0,0 +1,85 @@
+use crate::methods::DRAIN_COLLECT;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_range_full;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_lang_item;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_hir::ExprKind;
+use rustc_hir::LangItem;
+use rustc_hir::Path;
+use rustc_hir::QPath;
+use rustc_lint::LateContext;
+use rustc_middle::query::Key;
+use rustc_middle::ty;
+use rustc_middle::ty::Ty;
+use rustc_span::sym;
+use rustc_span::Symbol;
+
+/// Checks if both types match the given diagnostic item, e.g.:
+///
+/// `vec![1,2].drain(..).collect::<Vec<_>>()`
+/// ^^^^^^^^^ ^^^^^^ true
+/// `vec![1,2].drain(..).collect::<HashSet<_>>()`
+/// ^^^^^^^^^ ^^^^^^^^^^ false
+fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool {
+ if let Some(expr_adt_did) = expr.ty_adt_id()
+ && let Some(recv_adt_did) = recv.ty_adt_id()
+ {
+ cx.tcx.is_diagnostic_item(sym, expr_adt_did) && cx.tcx.is_diagnostic_item(sym, recv_adt_did)
+ } else {
+ false
+ }
+}
+
+/// Checks `std::{vec::Vec, collections::VecDeque}`.
+fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
+ (types_match_diagnostic_item(cx, expr, recv, sym::Vec)
+ || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque))
+ && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
+}
+
+/// Checks `std::string::String`
+fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
+ is_type_lang_item(cx, expr, LangItem::String)
+ && is_type_lang_item(cx, recv, LangItem::String)
+ && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
+}
+
+/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`.
+fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> {
+ types_match_diagnostic_item(cx, expr, recv, sym::HashSet)
+ .then_some("HashSet")
+ .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap"))
+ .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap"))
+}
+
+pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) {
+ let expr_ty = cx.typeck_results().expr_ty(expr);
+ let recv_ty = cx.typeck_results().expr_ty(recv);
+ let recv_ty_no_refs = recv_ty.peel_refs();
+
+ if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind
+ && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path)
+ .then_some("Vec")
+ .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
+ .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
+ {
+ let recv = snippet(cx, recv.span, "<expr>");
+ let sugg = if let ty::Ref(..) = recv_ty.kind() {
+ format!("std::mem::take({recv})")
+ } else {
+ format!("std::mem::take(&mut {recv})")
+ };
+
+ span_lint_and_sugg(
+ cx,
+ DRAIN_COLLECT,
+ expr.span,
+ &format!("you seem to be trying to move all elements into a new `{typename}`"),
+ "consider using `mem::take`",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+}
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 ffc3a4d78..e35fb12ed 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
@@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -23,21 +22,15 @@ pub(super) fn check<'tcx>(
let mut applicability = Applicability::MachineApplicable;
let expr_ty = cx.typeck_results().expr_ty(recv);
let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
- let mut needs_ref;
let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() {
- needs_ref = get_args_str.parse::<usize>().is_ok();
"slice"
} else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) {
- needs_ref = get_args_str.parse::<usize>().is_ok();
"Vec"
} else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) {
- needs_ref = get_args_str.parse::<usize>().is_ok();
"VecDeque"
} else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) {
- needs_ref = true;
"HashMap"
} else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) {
- needs_ref = true;
"BTreeMap"
} else {
return; // caller is not a type that we want to lint
@@ -45,18 +38,24 @@ pub(super) fn check<'tcx>(
let mut span = expr.span;
- // Handle the case where the result is immediately dereferenced
- // by not requiring ref and pulling the dereference into the
- // suggestion.
- if_chain! {
- if needs_ref;
- if let Some(parent) = get_parent_expr(cx, expr);
- if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind;
- then {
- needs_ref = false;
+ // Handle the case where the result is immediately dereferenced,
+ // either directly be the user, or as a result of a method call or the like
+ // 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
+ {
+ if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind {
+ // if the user explicitly dereferences the result, we can adjust
+ // the span to also include the deref part
span = parent.span;
}
- }
+ false
+ } else {
+ true
+ };
let mut_str = if is_mut { "_mut" } else { "" };
let borrow_str = if !needs_ref {
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
index ceee12784..121043104 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
@@ -20,9 +20,9 @@ pub(super) fn check<'tcx>(
let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() {
"slice"
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) {
- "Vec"
+ "`Vec`"
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) {
- "VecDeque"
+ "`VecDeque`"
} else {
iter_nth_zero::check(cx, expr, nth_recv, nth_arg);
return; // caller is not a type that we want to lint
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
index d1609eebf..e1f950d5a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
@@ -1,8 +1,8 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_trait_method;
use clippy_utils::source::snippet_with_applicability;
-use if_chain::if_chain;
+use clippy_utils::{is_lang_item_or_ctor, is_trait_method};
+use hir::{LangItem, OwnerNode};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -11,20 +11,21 @@ use rustc_span::sym;
use super::ITER_NTH_ZERO;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
- if_chain! {
- if is_trait_method(cx, expr, sym::Iterator);
- if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg);
- then {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- ITER_NTH_ZERO,
- expr.span,
- "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
- "try calling `.next()` instead of `.nth(0)`",
- format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)),
- applicability,
- );
- }
+ if let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id))
+ && let def_id = item.owner_id.to_def_id()
+ && is_trait_method(cx, expr, sym::Iterator)
+ && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg)
+ && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext)
+ {
+ let mut app = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ ITER_NTH_ZERO,
+ expr.span,
+ "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
+ "try calling `.next()` instead of `.nth(0)`",
+ format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut app)),
+ app,
+ );
}
}
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
new file mode 100644
index 000000000..576a58499
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -0,0 +1,55 @@
+use clippy_utils::{
+ diagnostics::span_lint_and_sugg,
+ is_from_proc_macro,
+ msrvs::{Msrv, ITERATOR_TRY_FOLD},
+ source::snippet_opt,
+ ty::implements_trait,
+};
+use rustc_errors::Applicability;
+use rustc_hir::{
+ def::{DefKind, Res},
+ Expr, ExprKind,
+};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_span::Span;
+
+use super::MANUAL_TRY_FOLD;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'tcx>,
+ init: &Expr<'_>,
+ acc: &Expr<'_>,
+ fold_span: Span,
+ msrv: &Msrv,
+) {
+ if !in_external_macro(cx.sess(), fold_span)
+ && msrv.meets(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, &[])
+ && let ExprKind::Call(path, [first, rest @ ..]) = init.kind
+ && let ExprKind::Path(qpath) = path.kind
+ && let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
+ && let ExprKind::Closure(closure) = acc.kind
+ && !is_from_proc_macro(cx, expr)
+ && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span))
+ {
+ let init_snip = rest
+ .is_empty()
+ .then_some(first.span)
+ .and_then(|span| snippet_opt(cx, span))
+ .unwrap_or("...".to_owned());
+
+ span_lint_and_sugg(
+ cx,
+ MANUAL_TRY_FOLD,
+ fold_span,
+ "usage of `Iterator::fold` on a type that implements `Try`",
+ "use `try_fold` instead",
+ format!("try_fold({init_snip}, {args_snip} ...)", ),
+ Applicability::HasPlaceholders,
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 9a594d964..24dbe8c1d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -14,6 +14,7 @@ mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
mod collapsible_str_replace;
+mod drain_collect;
mod err_expect;
mod expect_fun_call;
mod expect_used;
@@ -49,6 +50,7 @@ mod manual_next_back;
mod manual_ok_or;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
+mod manual_try_fold;
mod map_clone;
mod map_collect_result_unit;
mod map_err_ignore;
@@ -93,6 +95,7 @@ mod unnecessary_fold;
mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
+mod unnecessary_literal_unwrap;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unwrap_or_else_default;
@@ -275,6 +278,32 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
+ /// Checks for `.unwrap()` related calls on `Result`s and `Option`s that are constructed.
+ ///
+ /// ### Why is this bad?
+ /// It is better to write the value directly without the indirection.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// let val1 = Some(1).unwrap();
+ /// let val2 = Ok::<_, ()>(1).unwrap();
+ /// let val3 = Err::<(), _>(1).unwrap_err();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let val1 = 1;
+ /// let val2 = 1;
+ /// let val3 = 1;
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub UNNECESSARY_LITERAL_UNWRAP,
+ complexity,
+ "using `unwrap()` related calls on `Result` and `Option` constructors"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
///
/// ### Why is this bad?
@@ -485,6 +514,7 @@ declare_clippy_lint! {
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
/// option.map(|a| a + 1).unwrap_or(0);
+ /// option.map(|a| a > 10).unwrap_or(false);
/// result.map(|a| a + 1).unwrap_or_else(some_function);
/// ```
///
@@ -494,6 +524,7 @@ declare_clippy_lint! {
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
/// option.map_or(0, |a| a + 1);
+ /// option.is_some_and(|a| a > 10);
/// result.map_or_else(some_function, |a| a + 1);
/// ```
#[clippy::version = "1.45.0"]
@@ -3191,7 +3222,7 @@ declare_clippy_lint! {
/// let mut v = vec![1, 2, 3];
/// v.clear();
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub CLEAR_WITH_DRAIN,
nursery,
"calling `drain` in order to `clear` a container"
@@ -3220,6 +3251,71 @@ declare_clippy_lint! {
"manual reverse iteration of `DoubleEndedIterator`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `.drain()` that clear the collection, immediately followed by a call to `.collect()`.
+ ///
+ /// > "Collection" in this context refers to any type with a `drain` method:
+ /// > `Vec`, `VecDeque`, `BinaryHeap`, `HashSet`,`HashMap`, `String`
+ ///
+ /// ### Why is this bad?
+ /// Using `mem::take` is faster as it avoids the allocation.
+ /// When using `mem::take`, the old collection is replaced with an empty one and ownership of
+ /// the old collection is returned.
+ ///
+ /// ### Known issues
+ /// `mem::take(&mut vec)` is almost equivalent to `vec.drain(..).collect()`, except that
+ /// it also moves the **capacity**. The user might have explicitly written it this way
+ /// to keep the capacity on the original `Vec`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
+ /// v.drain(..).collect()
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::mem;
+ /// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
+ /// mem::take(v)
+ /// }
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub DRAIN_COLLECT,
+ perf,
+ "calling `.drain(..).collect()` to move all elements into a new collection"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `Iterator::fold` with a type that implements `Try`.
+ ///
+ /// ### Why is this bad?
+ /// The code should use `try_fold` instead, which short-circuits on failure, thus opening the
+ /// door for additional optimizations not possible with `fold` as rustc can guarantee the
+ /// function is never called on `None`, `Err`, etc., alleviating otherwise necessary checks. It's
+ /// also slightly more idiomatic.
+ ///
+ /// ### Known issues
+ /// This lint doesn't take into account whether a function does something on the failure case,
+ /// i.e., whether short-circuiting will affect behavior. Refactoring to `try_fold` is not
+ /// desirable in those cases.
+ ///
+ /// ### Example
+ /// ```rust
+ /// vec![1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// vec![1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i));
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub MANUAL_TRY_FOLD,
+ perf,
+ "checks for usage of `Iterator::fold` with a type that implements `Try`"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@@ -3349,6 +3445,9 @@ impl_lint_pass!(Methods => [
SUSPICIOUS_COMMAND_ARG_SPACE,
CLEAR_WITH_DRAIN,
MANUAL_NEXT_BACK,
+ UNNECESSARY_LITERAL_UNWRAP,
+ DRAIN_COLLECT,
+ MANUAL_TRY_FOLD,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3578,6 +3677,9 @@ impl Methods {
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
}
},
+ Some(("drain", recv, args, ..)) => {
+ drain_collect::check(cx, args, expr, recv);
+ }
_ => {},
}
},
@@ -3606,12 +3708,18 @@ impl Methods {
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
}
},
- ("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),
- _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
+ ("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),
+ _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
+ }
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
+ },
+ ("expect_err", [_]) => {
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
+ expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
},
- ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
extend_with_drain::check(cx, expr, recv, arg);
@@ -3632,7 +3740,10 @@ impl Methods {
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
_ => {},
},
- ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
+ ("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", [_]) => {
if let Some(("inspect", _, [_], span2, _)) = method_call(recv) {
inspect_for_each::check(cx, expr, span2);
@@ -3816,28 +3927,41 @@ impl Methods {
},
_ => {},
}
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
},
- ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
- ("unwrap_or", [u_arg]) => match method_call(recv) {
- Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
- manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
- },
- Some(("map", m_recv, [m_arg], span, _)) => {
- option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
- },
- Some(("then_some", t_recv, [t_arg], _, _)) => {
- obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
- },
- _ => {},
+ ("unwrap_err", []) => {
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
+ unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
},
- ("unwrap_or_else", [u_arg]) => match method_call(recv) {
- Some(("map", recv, [map_arg], _, _))
- if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
- _ => {
- unwrap_or_else_default::check(cx, expr, recv, u_arg);
- unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
- },
+ ("unwrap_or", [u_arg]) => {
+ match method_call(recv) {
+ Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
+ manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
+ },
+ Some(("map", m_recv, [m_arg], span, _)) => {
+ option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
+ },
+ Some(("then_some", t_recv, [t_arg], _, _)) => {
+ obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
+ },
+ _ => {},
+ }
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
+ },
+ ("unwrap_or_default", []) => {
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
+ }
+ ("unwrap_or_else", [u_arg]) => {
+ match method_call(recv) {
+ Some(("map", recv, [map_arg], _, _))
+ if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
+ _ => {
+ unwrap_or_else_default::check(cx, expr, recv, u_arg);
+ unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
+ },
+ }
+ unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("zip", [arg]) => {
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
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 6841aaf62..8ca7af810 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -16,7 +16,7 @@ use rustc_hir::{
};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::{self, AssocKind, Clause, EarlyBinder, GenericArg, GenericArgKind, PredicateKind, Ty};
+use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol};
@@ -175,7 +175,7 @@ fn check_collect_into_intoiterator<'tcx>(
.caller_bounds()
.into_iter()
.filter_map(|p| {
- if let PredicateKind::Clause(Clause::Trait(t)) = p.kind().skip_binder()
+ 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 {
@@ -215,7 +215,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty:
&& let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- cx.tcx.mk_projection(into_iter_item_proj.def_id, into_iter_item_proj.substs)
+ Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.substs)
)
{
iter_item_ty == into_iter_item_ty
@@ -238,10 +238,10 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -
.associated_items(iter_trait)
.find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
&& let substs = cx.tcx.mk_substs(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
- && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs)
+ && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, substs)
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
{
- item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id))
+ item_ty == EarlyBinder::bind(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id))
} else {
false
}
@@ -322,7 +322,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
- if let ExprKind::MethodCall(method_name, recv, [args @ ..], _) = &expr.kind {
+ if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
self.visit_expr(recv);
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 4c6328481..f4f158c04 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,19 +1,26 @@
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;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_path, Visitor};
+use rustc_hir::ExprKind;
+use rustc_hir::Node;
+use rustc_hir::PatKind;
+use rustc_hir::QPath;
use rustc_hir::{self, HirId, Path};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_span::source_map::Span;
-use rustc_span::{sym, Symbol};
+use rustc_span::sym;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or()` for `Option`s
+#[expect(clippy::too_many_arguments)]
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &rustc_hir::Expr<'_>,
@@ -22,12 +29,27 @@ pub(super) fn check<'tcx>(
unwrap_recv: &rustc_hir::Expr<'_>,
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
map_span: Span,
+ msrv: &Msrv,
) {
// lint if the caller of `map()` is an `Option`
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) {
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
- // Do not lint if the `map` argument uses identifiers in the `map`
- // argument that are also used in the `unwrap_or` argument
+ // Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
+ // borrowck errors, see #10579 for one such instance.
+ // In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
+ // ```
+ // let x = vec![1, 2];
+ // x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
+ // ```
+ // This compiles, but changing it to `map_or` will produce a compile error:
+ // ```
+ // let x = vec![1, 2];
+ // x.get(0..1).map_or(x, |s| s.to_vec())
+ // ^ moving `x` here
+ // ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
+ // ```
+ // So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
+ // before the call to `unwrap_or`.
let mut unwrap_visitor = UnwrapVisitor {
cx,
@@ -35,14 +57,18 @@ pub(super) fn check<'tcx>(
};
unwrap_visitor.visit_expr(unwrap_arg);
- let mut map_expr_visitor = MapExprVisitor {
+ let mut reference_visitor = ReferenceVisitor {
cx,
identifiers: unwrap_visitor.identifiers,
- found_identifier: false,
+ found_reference: false,
+ unwrap_or_span: unwrap_arg.span,
};
- map_expr_visitor.visit_expr(map_arg);
- if map_expr_visitor.found_identifier {
+ let map = cx.tcx.hir();
+ let body = map.body(map.body_owned_by(map.enclosing_body_owner(expr.hir_id)));
+ reference_visitor.visit_body(body);
+
+ if reference_visitor.found_reference {
return;
}
}
@@ -51,16 +77,29 @@ pub(super) fn check<'tcx>(
return;
}
+ // is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
+ let suggest_is_some_and = msrv.meets(msrvs::OPTION_IS_SOME_AND)
+ && matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
+ if matches!(lit.node, rustc_ast::LitKind::Bool(false)));
+
let mut applicability = Applicability::MachineApplicable;
// get snippet for unwrap_or()
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
// lint message
// comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type.
- let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
+ let arg = if unwrap_snippet == "None" {
+ "None"
+ } else if suggest_is_some_and {
+ "false"
+ } else {
+ "<a>"
+ };
let unwrap_snippet_none = unwrap_snippet == "None";
let suggest = if unwrap_snippet_none {
"and_then(<f>)"
+ } else if suggest_is_some_and {
+ "is_some_and(<f>)"
} else {
"map_or(<a>, <f>)"
};
@@ -75,12 +114,18 @@ pub(super) fn check<'tcx>(
let mut suggestion = vec![
(
map_span,
- String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
+ String::from(if unwrap_snippet_none {
+ "and_then"
+ } else if suggest_is_some_and {
+ "is_some_and"
+ } else {
+ "map_or"
+ }),
),
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
];
- if !unwrap_snippet_none {
+ if !unwrap_snippet_none && !suggest_is_some_and {
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
}
@@ -91,14 +136,19 @@ pub(super) fn check<'tcx>(
struct UnwrapVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
- identifiers: FxHashSet<Symbol>,
+ identifiers: FxHashSet<HirId>,
}
impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::All;
- fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
- self.identifiers.insert(ident(path));
+ fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
+ if let Res::Local(local_id) = path.res
+ && let Some(Node::Pat(pat)) = self.cx.tcx.hir().find(local_id)
+ && let PatKind::Binding(_, local_id, ..) = pat.kind
+ {
+ self.identifiers.insert(local_id);
+ }
walk_path(self, path);
}
@@ -107,32 +157,35 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
}
}
-struct MapExprVisitor<'a, 'tcx> {
+struct ReferenceVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
- identifiers: FxHashSet<Symbol>,
- found_identifier: bool,
+ identifiers: FxHashSet<HirId>,
+ found_reference: bool,
+ unwrap_or_span: Span,
}
-impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::All;
-
- fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
- if self.identifiers.contains(&ident(path)) {
- self.found_identifier = true;
- return;
+ fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) {
+ // If we haven't found a reference yet, check if this references
+ // one of the locals that was moved in the `unwrap_or` argument.
+ // We are only interested in exprs that appear before the `unwrap_or` call.
+ if !self.found_reference {
+ if expr.span < self.unwrap_or_span
+ && let ExprKind::Path(ref path) = expr.kind
+ && let QPath::Resolved(_, path) = path
+ && let Res::Local(local_id) = path.res
+ && let Some(Node::Pat(pat)) = self.cx.tcx.hir().find(local_id)
+ && let PatKind::Binding(_, local_id, ..) = pat.kind
+ && self.identifiers.contains(&local_id)
+ {
+ self.found_reference = true;
+ }
+ rustc_hir::intravisit::walk_expr(self, expr);
}
- walk_path(self, path);
}
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
}
-
-fn ident(path: &Path<'_>) -> Symbol {
- path.segments
- .last()
- .expect("segments should be composed of at least 1 element")
- .ident
- .name
-}
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 5ea12c441..88a3c2620 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -289,7 +289,7 @@ fn parse_iter_usage<'tcx>(
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
- let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else {
+ let ExprKind::MethodCall(name, _, args, _) = e.kind else {
return None;
};
let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
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 5a3d12fd7..8ec15a1c1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -7,71 +7,120 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_lint::LateContext;
+use rustc_middle::ty;
use rustc_span::{source_map::Span, sym};
use super::UNNECESSARY_FOLD;
-pub(super) fn check(
+/// Do we need to suggest turbofish when suggesting a replacement method?
+/// Changing `fold` to `sum` needs it sometimes when the return type can't be
+/// inferred. This checks for some common cases where it can be safely omitted
+fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+ let parent = cx.tcx.hir().get_parent(expr.hir_id);
+
+ // some common cases where turbofish isn't needed:
+ // - assigned to a local variable with a type annotation
+ if let hir::Node::Local(local) = parent
+ && local.ty.is_some()
+ {
+ return false;
+ }
+
+ // - part of a function call argument, can be inferred from the function signature (provided that
+ // the parameter is not a generic type parameter)
+ if let hir::Node::Expr(parent_expr) = parent
+ && let hir::ExprKind::Call(recv, args) = parent_expr.kind
+ && let hir::ExprKind::Path(ref qpath) = recv.kind
+ && let Some(fn_def_id) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
+ && let fn_sig = cx.tcx.fn_sig(fn_def_id).skip_binder().skip_binder()
+ && let Some(arg_pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
+ && let Some(ty) = fn_sig.inputs().get(arg_pos)
+ && !matches!(ty.kind(), ty::Param(_))
+ {
+ return false;
+ }
+
+ // if it's neither of those, stay on the safe side and suggest turbofish,
+ // even if it could work!
+ true
+}
+
+#[derive(Copy, Clone)]
+struct Replacement {
+ method_name: &'static str,
+ has_args: bool,
+ has_generic_return: bool,
+}
+
+fn check_fold_with_op(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
- init: &hir::Expr<'_>,
acc: &hir::Expr<'_>,
fold_span: Span,
+ op: hir::BinOpKind,
+ replacement: Replacement,
) {
- fn check_fold_with_op(
- cx: &LateContext<'_>,
- expr: &hir::Expr<'_>,
- acc: &hir::Expr<'_>,
- fold_span: Span,
- op: hir::BinOpKind,
- replacement_method_name: &str,
- replacement_has_args: bool,
- ) {
- if_chain! {
- // Extract the body of the closure passed to fold
- if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
- let closure_body = cx.tcx.hir().body(body);
- let closure_expr = peel_blocks(closure_body.value);
-
- // Check if the closure body is of the form `acc <op> some_expr(x)`
- if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
- if bin_op.node == op;
-
- // Extract the names of the two arguments to the closure
- if let [param_a, param_b] = closure_body.params;
- if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
- if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
-
- if path_to_local_id(left_expr, first_arg_id);
- if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- let sugg = if replacement_has_args {
- format!(
- "{replacement_method_name}(|{second_arg_ident}| {r})",
- r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
- )
- } else {
- format!(
- "{replacement_method_name}()",
- )
- };
-
- span_lint_and_sugg(
- cx,
- UNNECESSARY_FOLD,
- fold_span.with_hi(expr.span.hi()),
- // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
- "this `.fold` can be written more succinctly using another method",
- "try",
- sugg,
- applicability,
- );
- }
+ if_chain! {
+ // Extract the body of the closure passed to fold
+ if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
+ let closure_body = cx.tcx.hir().body(body);
+ let closure_expr = peel_blocks(closure_body.value);
+
+ // Check if the closure body is of the form `acc <op> some_expr(x)`
+ if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
+ if bin_op.node == op;
+
+ // Extract the names of the two arguments to the closure
+ if let [param_a, param_b] = closure_body.params;
+ if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
+ if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
+
+ if path_to_local_id(left_expr, first_arg_id);
+ if replacement.has_args || path_to_local_id(right_expr, second_arg_id);
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+
+ let turbofish = if replacement.has_generic_return {
+ format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs())
+ } else {
+ String::new()
+ };
+
+ let sugg = if replacement.has_args {
+ format!(
+ "{method}{turbofish}(|{second_arg_ident}| {r})",
+ method = replacement.method_name,
+ r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
+ )
+ } else {
+ format!(
+ "{method}{turbofish}()",
+ method = replacement.method_name,
+ )
+ };
+
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_FOLD,
+ fold_span.with_hi(expr.span.hi()),
+ // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
+ "this `.fold` can be written more succinctly using another method",
+ "try",
+ sugg,
+ applicability,
+ );
}
}
+}
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ init: &hir::Expr<'_>,
+ acc: &hir::Expr<'_>,
+ fold_span: Span,
+) {
// Check that this is a call to Iterator::fold rather than just some function called fold
if !is_trait_method(cx, expr, sym::Iterator) {
return;
@@ -80,11 +129,59 @@ pub(super) fn check(
// Check if the first argument to .fold is a suitable literal
if let hir::ExprKind::Lit(lit) = init.kind {
match lit.node {
- ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
- ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
- ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false),
+ ast::LitKind::Bool(false) => {
+ check_fold_with_op(
+ cx,
+ expr,
+ acc,
+ fold_span,
+ hir::BinOpKind::Or,
+ Replacement {
+ has_args: true,
+ has_generic_return: false,
+ method_name: "any",
+ },
+ );
+ },
+ ast::LitKind::Bool(true) => {
+ check_fold_with_op(
+ cx,
+ expr,
+ acc,
+ fold_span,
+ hir::BinOpKind::And,
+ Replacement {
+ has_args: true,
+ has_generic_return: false,
+ method_name: "all",
+ },
+ );
+ },
+ ast::LitKind::Int(0, _) => check_fold_with_op(
+ cx,
+ expr,
+ acc,
+ fold_span,
+ hir::BinOpKind::Add,
+ Replacement {
+ has_args: false,
+ has_generic_return: needs_turbofish(cx, expr),
+ method_name: "sum",
+ },
+ ),
ast::LitKind::Int(1, _) => {
- check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false);
+ check_fold_with_op(
+ cx,
+ expr,
+ acc,
+ fold_span,
+ hir::BinOpKind::Mul,
+ Replacement {
+ has_args: false,
+ has_generic_return: needs_turbofish(cx, expr),
+ method_name: "product",
+ },
+ );
},
_ => (),
}
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
new file mode 100644
index 000000000..ea9b894b6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -0,0 +1,126 @@
+use clippy_utils::{diagnostics::span_lint_and_then, is_res_lang_ctor, last_path_segment, path_res, MaybePath};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_middle::ty::print::with_forced_trimmed_paths;
+
+use super::UNNECESSARY_LITERAL_UNWRAP;
+
+fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a>> {
+ let args = args?;
+
+ if args.len() <= index {
+ return None;
+ }
+
+ match args[index] {
+ hir::GenericArg::Type(ty) => match ty.kind {
+ hir::TyKind::Infer => None,
+ _ => Some(ty),
+ },
+ _ => None,
+ }
+}
+
+#[expect(clippy::too_many_lines)]
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ recv: &hir::Expr<'_>,
+ method: &str,
+ args: &[hir::Expr<'_>],
+) {
+ let init = clippy_utils::expr_or_init(cx, recv);
+
+ let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
+ let Some(qpath) = call.qpath_opt() else { return };
+
+ let args = last_path_segment(qpath).args.map(|args| args.args);
+ let res = cx.qpath_res(qpath, call.hir_id());
+
+ if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) {
+ ("Some", call_args, get_ty_from_args(args, 0))
+ } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) {
+ ("Ok", call_args, get_ty_from_args(args, 0))
+ } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) {
+ ("Err", call_args, get_ty_from_args(args, 1))
+ } else {
+ return;
+ }
+ } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) {
+ let call_args: &[hir::Expr<'_>] = &[];
+ ("None", call_args, None)
+ } else {
+ return;
+ };
+
+ let help_message = format!("used `{method}()` on `{constructor}` value");
+ let suggestion_message = format!("remove the `{constructor}` and `{method}()`");
+
+ span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, &help_message, |diag| {
+ let suggestions = match (constructor, method, ty) {
+ ("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]),
+ ("None", "expect", _) => Some(vec![
+ (expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()),
+ (expr.span.with_lo(args[0].span.hi()), ")".to_string()),
+ ]),
+ ("None", "unwrap_or_default", _) => {
+ let ty = cx.typeck_results().expr_ty(expr);
+ let default_ty_string = if let ty::Adt(def, ..) = ty.kind() {
+ with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did())))
+ } else {
+ "Default".to_string()
+ };
+ Some(vec![(expr.span, format!("{default_ty_string}::default()"))])
+ },
+ ("None", "unwrap_or", _) => Some(vec![
+ (expr.span.with_hi(args[0].span.lo()), String::new()),
+ (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()),
+ (expr.span.with_lo(args[0].span.hi()), String::new()),
+ ]),
+ _ => None,
+ },
+ _ if call_args.is_empty() => None,
+ (_, _, Some(_)) => None,
+ ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![
+ (
+ recv.span.with_hi(call_args[0].span.lo()),
+ "panic!(\"{:?}\", ".to_string(),
+ ),
+ (expr.span.with_lo(call_args[0].span.hi()), ")".to_string()),
+ ]),
+ ("Ok", "expect_err", None) | ("Err", "expect", None) => Some(vec![
+ (
+ recv.span.with_hi(call_args[0].span.lo()),
+ "panic!(\"{1}: {:?}\", ".to_string(),
+ ),
+ (call_args[0].span.with_lo(args[0].span.lo()), ", ".to_string()),
+ ]),
+ (_, _, None) => Some(vec![
+ (recv.span.with_hi(call_args[0].span.lo()), String::new()),
+ (expr.span.with_lo(call_args[0].span.hi()), String::new()),
+ ]),
+ };
+
+ match (init.span == recv.span, suggestions) {
+ (true, Some(suggestions)) => {
+ diag.multipart_suggestion(suggestion_message, suggestions, Applicability::MachineApplicable);
+ },
+ _ => {
+ diag.span_help(init.span, suggestion_message);
+ },
+ }
+ });
+}
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 67b7d3691..6bd5e9e88 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
@@ -14,7 +14,7 @@ use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_middle::ty::{self, ClauseKind, EarlyBinder, ParamTy, ProjectionPredicate, TraitPredicate, Ty};
use rustc_span::{sym, Symbol};
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
@@ -144,6 +144,11 @@ fn check_addr_of_expr(
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty);
+ // Make sure that it's actually calling the right `.to_string()`, (#10033)
+ // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow)
+ // but that's ok for Cow::into_owned specifically)
+ if cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty
+ || is_cow_into_owned(cx, method_name, method_def_id);
then {
if n_receiver_refs > 0 {
span_lint_and_sugg(
@@ -265,7 +270,7 @@ fn check_other_call_arg<'tcx>(
if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
Some((n_refs, receiver_ty))
} else if trait_predicate.def_id() != deref_trait_id {
- Some((1, cx.tcx.mk_ref(
+ Some((1, Ty::new_ref(cx.tcx,
cx.tcx.lifetimes.re_erased,
ty::TypeAndMut {
ty: receiver_ty,
@@ -345,12 +350,12 @@ fn get_input_traits_and_projections<'tcx>(
let mut projection_predicates = Vec::new();
for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
match predicate.kind().skip_binder() {
- PredicateKind::Clause(Clause::Trait(trait_predicate)) => {
+ ClauseKind::Trait(trait_predicate) => {
if trait_predicate.trait_ref.self_ty() == input {
trait_predicates.push(trait_predicate);
}
},
- PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
+ ClauseKind::Projection(projection_predicate) => {
if projection_predicate.projection_ty.self_ty() == input {
projection_predicates.push(projection_predicate);
}
@@ -407,7 +412,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
.caller_bounds().iter().filter(|predicate| {
- if let PredicateKind::Clause(Clause::Trait(trait_predicate))
+ if let ClauseKind::Trait(trait_predicate)
= predicate.kind().skip_binder()
&& trait_predicate.trait_ref.self_ty() == *param_ty
{
@@ -428,7 +433,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
}));
if trait_predicates.any(|predicate| {
- let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
+ let predicate = EarlyBinder::bind(predicate).subst(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)
}) {
@@ -438,7 +443,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
let output_ty = fn_sig.output();
if output_ty.contains(*param_ty) {
if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions(
- new_subst, cx.param_env, EarlyBinder(output_ty)) {
+ new_subst, cx.param_env, EarlyBinder::bind(output_ty)) {
expr = parent_expr;
ty = new_ty;
continue;
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
new file mode 100644
index 000000000..d49bb0ca6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -0,0 +1,153 @@
+use clippy_utils::{diagnostics::span_lint, is_from_proc_macro};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{
+ def::{DefKind, Res},
+ intravisit::{walk_item, Visitor},
+ GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind,
+};
+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::Span;
+use std::borrow::Cow;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for idents which comprise of a single letter.
+ ///
+ /// Note: This lint can be very noisy when enabled; it may be desirable to only enable it
+ /// temporarily.
+ ///
+ /// ### Why is this bad?
+ /// In many cases it's not, but at times it can severely hinder readability. Some codebases may
+ /// wish to disallow this to improve readability.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// for m in movies {
+ /// let title = m.t;
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// for movie in movies {
+ /// let title = movie.title;
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub MIN_IDENT_CHARS,
+ restriction,
+ "disallows idents that are too short"
+}
+impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]);
+
+#[derive(Clone)]
+pub struct MinIdentChars {
+ pub allowed_idents_below_min_chars: FxHashSet<String>,
+ pub min_ident_chars_threshold: u64,
+}
+
+impl MinIdentChars {
+ #[expect(clippy::cast_possible_truncation)]
+ fn is_ident_too_short(&self, cx: &LateContext<'_>, str: &str, span: Span) -> bool {
+ !in_external_macro(cx.sess(), span)
+ && str.len() <= self.min_ident_chars_threshold as usize
+ && !str.starts_with('_')
+ && !str.is_empty()
+ && self.allowed_idents_below_min_chars.get(&str.to_owned()).is_none()
+ }
+}
+
+impl LateLintPass<'_> for MinIdentChars {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ if self.min_ident_chars_threshold == 0 {
+ return;
+ }
+
+ walk_item(&mut IdentVisitor { conf: self, cx }, item);
+ }
+
+ // This is necessary as `Node::Pat`s are not visited in `visit_id`. :/
+ fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
+ if let PatKind::Binding(_, _, ident, ..) = pat.kind
+ && let str = ident.as_str()
+ && self.is_ident_too_short(cx, str, ident.span)
+ {
+ emit_min_ident_chars(self, cx, str, ident.span);
+ }
+ }
+}
+
+struct IdentVisitor<'cx, 'tcx> {
+ conf: &'cx MinIdentChars,
+ cx: &'cx LateContext<'tcx>,
+}
+
+impl Visitor<'_> for IdentVisitor<'_, '_> {
+ fn visit_id(&mut self, hir_id: HirId) {
+ let Self { conf, cx } = *self;
+ // FIXME(#112534) Reimplementation of `find`, as it uses indexing, which can (and will in
+ // async functions, or really anything async) panic. This should probably be fixed on the
+ // rustc side, this is just a temporary workaround.
+ let node = if hir_id.local_id == ItemLocalId::from_u32(0) {
+ // In this case, we can just use `find`, `Owner`'s `node` field is private anyway so we can't
+ // reimplement it even if we wanted to
+ cx.tcx.hir().find(hir_id)
+ } else {
+ let Some(owner) = cx.tcx.hir_owner_nodes(hir_id.owner).as_owner() else {
+ return;
+ };
+ owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node)
+ };
+ let Some(node) = node else {
+ return;
+ };
+ let Some(ident) = node.ident() else {
+ return;
+ };
+
+ 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 {
+ return;
+ }
+ // `struct Awa<T>(T)`
+ // ^
+ if let Node::PathSegment(path) = node {
+ 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())
+ {
+ return;
+ }
+ }
+ // `struct Awa<T>(T)`
+ // ^
+ if let Node::GenericParam(generic_param) = node
+ && let GenericParamKind::Type { .. } = generic_param.kind
+ {
+ return;
+ }
+
+ if is_from_proc_macro(cx, &ident) {
+ return;
+ }
+
+ emit_min_ident_chars(conf, cx, str, ident.span);
+ }
+ }
+}
+
+fn emit_min_ident_chars(conf: &MinIdentChars, cx: &impl LintContext, ident: &str, span: Span) {
+ let help = if conf.min_ident_chars_threshold == 1 {
+ Cow::Borrowed("this ident consists of a single char")
+ } else {
+ Cow::Owned(format!(
+ "this ident is too short ({} <= {})",
+ ident.len(),
+ conf.min_ident_chars_threshold,
+ ))
+ };
+ span_lint(cx, MIN_IDENT_CHARS, span, &help);
+}
diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs
index 4f967755b..e0904f17b 100644
--- a/src/tools/clippy/clippy_lints/src/minmax.rs
+++ b/src/tools/clippy/clippy_lints/src/minmax.rs
@@ -66,7 +66,7 @@ enum MinMax {
Max,
}
-fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
+fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
match expr.kind {
ExprKind::Call(path, args) => {
if let ExprKind::Path(ref qpath) = path.kind {
@@ -99,12 +99,12 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
}
}
-fn fetch_const<'a>(
- cx: &LateContext<'_>,
+fn fetch_const<'a, 'tcx>(
+ cx: &LateContext<'tcx>,
receiver: Option<&'a Expr<'a>>,
args: &'a [Expr<'a>],
m: MinMax,
-) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
+) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
let mut args = receiver.into_iter().chain(args);
let first_arg = args.next()?;
let second_arg = args.next()?;
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
index ddb8b9173..9151cc633 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
@@ -13,7 +13,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_sni
return;
}
let mut seen = (false, false);
- for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
+ for ch in &lit_snip.as_bytes()[2..=maybe_last_sep_idx] {
match ch {
b'a'..=b'f' => seen.0 = true,
b'A'..=b'F' => seen.1 = true,
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 78be6b9e2..b226b8781 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -2,6 +2,7 @@ mod builtin_type_shadow;
mod double_neg;
mod literal_suffix;
mod mixed_case_hex_literals;
+mod redundant_at_rest_pattern;
mod redundant_pattern;
mod unneeded_field_pattern;
mod unneeded_wildcard_pattern;
@@ -318,6 +319,36 @@ declare_clippy_lint! {
"tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `[all @ ..]` patterns.
+ ///
+ /// ### Why is this bad?
+ /// In all cases, `all` works fine and can often make code simpler, as you possibly won't need
+ /// to convert from say a `Vec` to a slice by dereferencing.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if let [all @ ..] = &*v {
+ /// // NOTE: Type is a slice here
+ /// println!("all elements: {all:#?}");
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// if let all = v {
+ /// // NOTE: Type is a `Vec` here
+ /// println!("all elements: {all:#?}");
+ /// }
+ /// // or
+ /// println!("all elements: {v:#?}");
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub REDUNDANT_AT_REST_PATTERN,
+ complexity,
+ "checks for `[all @ ..]` where `all` would suffice"
+}
+
declare_lint_pass!(MiscEarlyLints => [
UNNEEDED_FIELD_PATTERN,
DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -329,6 +360,7 @@ declare_lint_pass!(MiscEarlyLints => [
BUILTIN_TYPE_SHADOW,
REDUNDANT_PATTERN,
UNNEEDED_WILDCARD_PATTERN,
+ REDUNDANT_AT_REST_PATTERN,
]);
impl EarlyLintPass for MiscEarlyLints {
@@ -339,8 +371,13 @@ impl EarlyLintPass for MiscEarlyLints {
}
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
+ if in_external_macro(cx.sess(), pat.span) {
+ return;
+ }
+
unneeded_field_pattern::check(cx, pat);
redundant_pattern::check(cx, pat);
+ redundant_at_rest_pattern::check(cx, pat);
unneeded_wildcard_pattern::check(cx, pat);
}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/redundant_at_rest_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/redundant_at_rest_pattern.rs
new file mode 100644
index 000000000..0c81ee5ec
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/misc_early/redundant_at_rest_pattern.rs
@@ -0,0 +1,26 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::{Pat, PatKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+
+use super::REDUNDANT_AT_REST_PATTERN;
+
+pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
+ if !in_external_macro(cx.sess(), pat.span)
+ && let PatKind::Slice(slice) = &pat.kind
+ && let [one] = &**slice
+ && let PatKind::Ident(annotation, ident, Some(rest)) = &one.kind
+ && let PatKind::Rest = rest.kind
+ {
+ span_lint_and_sugg(
+ cx,
+ REDUNDANT_AT_REST_PATTERN,
+ pat.span,
+ "using a rest pattern to bind an entire slice to a local",
+ "this is better represented with just the binding",
+ format!("{}{ident}", annotation.prefix_str()),
+ Applicability::MachineApplicable,
+ );
+ }
+}
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 9de4b56b7..28e041dee 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
@@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
then {
// get the name and span of the generic parameters in the Impl
let mut impl_params = Vec::new();
- for p in generic_args.args.iter() {
+ for p in generic_args.args {
match p {
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
impl_params.push((path.segments[0].ident.to_string(), path.span)),
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 2214a568d..4dbb79334 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -36,7 +36,7 @@ declare_clippy_lint! {
/// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
/// }
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub MISSING_ASSERT_MESSAGE,
restriction,
"checks assertions without a custom panic message"
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 f1831a304..3b7eccad7 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
@@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
- cx.tcx.sess.span_err(span, err.as_ref());
+ cx.tcx.sess.span_err(span, err);
}
} else {
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
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
new file mode 100644
index 000000000..497514fbc
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -0,0 +1,238 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::{
+ diagnostics::span_lint_and_then,
+ is_path_lang_item, paths,
+ ty::match_type,
+ visitors::{for_each_expr, Visitable},
+};
+use rustc_ast::LitKind;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::Block;
+use rustc_hir::{
+ def::{DefKind, Res},
+ Expr, ImplItemKind, LangItem, Node,
+};
+use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind};
+use rustc_hir::{ImplItem, Item, VariantData};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
+use rustc_middle::ty::TypeckResults;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, Span, Symbol};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual [`core::fmt::Debug`](https://doc.rust-lang.org/core/fmt/trait.Debug.html) implementations that do not use all fields.
+ ///
+ /// ### Why is this bad?
+ /// A common mistake is to forget to update manual `Debug` implementations when adding a new field
+ /// to a struct or a new variant to an enum.
+ ///
+ /// At the same time, it also acts as a style lint to suggest using [`core::fmt::DebugStruct::finish_non_exhaustive`](https://doc.rust-lang.org/core/fmt/struct.DebugStruct.html#method.finish_non_exhaustive)
+ /// for the times when the user intentionally wants to leave out certain fields (e.g. to hide implementation details).
+ ///
+ /// ### Known problems
+ /// This lint works based on the `DebugStruct` helper types provided by the `Formatter`,
+ /// so this won't detect `Debug` impls that use the `write!` macro.
+ /// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries
+ /// to be on the conservative side and not lint in those cases in an attempt to prevent false positives.
+ ///
+ /// This lint also does not look through function calls, so calling a function does not consider fields
+ /// used inside of that function as used by the `Debug` impl.
+ ///
+ /// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive`
+ /// method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum,
+ /// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::fmt;
+ /// struct Foo {
+ /// data: String,
+ /// // implementation detail
+ /// hidden_data: i32
+ /// }
+ /// impl fmt::Debug for Foo {
+ /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// formatter
+ /// .debug_struct("Foo")
+ /// .field("data", &self.data)
+ /// .finish()
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::fmt;
+ /// struct Foo {
+ /// data: String,
+ /// // implementation detail
+ /// hidden_data: i32
+ /// }
+ /// impl fmt::Debug for Foo {
+ /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// formatter
+ /// .debug_struct("Foo")
+ /// .field("data", &self.data)
+ /// .finish_non_exhaustive()
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub MISSING_FIELDS_IN_DEBUG,
+ pedantic,
+ "missing fields in manual `Debug` implementation"
+}
+declare_lint_pass!(MissingFieldsInDebug => [MISSING_FIELDS_IN_DEBUG]);
+
+fn report_lints(cx: &LateContext<'_>, span: Span, span_notes: Vec<(Span, &'static str)>) {
+ span_lint_and_then(
+ cx,
+ MISSING_FIELDS_IN_DEBUG,
+ span,
+ "manual `Debug` impl does not include all fields",
+ |diag| {
+ for (span, note) in span_notes {
+ diag.span_note(span, note);
+ }
+ diag.help("consider including all fields in this `Debug` impl")
+ .help("consider calling `.finish_non_exhaustive()` if you intend to ignore fields");
+ },
+ );
+}
+
+/// Checks if we should lint in a block of code
+///
+/// The way we check for this condition is by checking if there is
+/// a call to `Formatter::debug_struct` but no call to `.finish_non_exhaustive()`.
+fn should_lint<'tcx>(
+ cx: &LateContext<'tcx>,
+ typeck_results: &TypeckResults<'tcx>,
+ block: impl Visitable<'tcx>,
+) -> bool {
+ // Is there a call to `DebugStruct::finish_non_exhaustive`? Don't lint if there is.
+ let mut has_finish_non_exhaustive = false;
+ // Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
+ let mut has_debug_struct = false;
+
+ for_each_expr(block, |expr| {
+ 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) {
+ has_debug_struct = true;
+ } else if path.ident.name == sym!(finish_non_exhaustive) && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) {
+ has_finish_non_exhaustive = true;
+ }
+ }
+ ControlFlow::<!, _>::Continue(())
+ });
+
+ !has_finish_non_exhaustive && has_debug_struct
+}
+
+/// Checks if the given expression is a call to `DebugStruct::field`
+/// and the first argument to it is a string literal and if so, returns it
+///
+/// Example: `.field("foo", ....)` returns `Some("foo")`
+fn as_field_call<'tcx>(
+ cx: &LateContext<'tcx>,
+ typeck_results: &TypeckResults<'tcx>,
+ expr: &Expr<'_>,
+) -> 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)
+ && path.ident.name == sym::field
+ && let ExprKind::Lit(lit) = &debug_field.kind
+ && let LitKind::Str(sym, ..) = lit.node
+ {
+ Some(sym)
+ } else {
+ None
+ }
+}
+
+/// Attempts to find unused fields assuming that the item is a struct
+fn check_struct<'tcx>(
+ cx: &LateContext<'tcx>,
+ typeck_results: &TypeckResults<'tcx>,
+ block: &'tcx Block<'tcx>,
+ self_ty: Ty<'tcx>,
+ item: &'tcx Item<'tcx>,
+ data: &VariantData<'_>,
+) {
+ // Is there a "direct" field access anywhere (i.e. self.foo)?
+ // We don't want to lint if there is not, because the user might have
+ // a newtype struct and use fields from the wrapped type only.
+ let mut has_direct_field_access = false;
+ let mut field_accesses = FxHashSet::default();
+
+ for_each_expr(block, |expr| {
+ if let ExprKind::Field(target, ident) = expr.kind
+ && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
+ && target_ty == self_ty
+ {
+ field_accesses.insert(ident.name);
+ has_direct_field_access = true;
+ } else if let Some(sym) = as_field_call(cx, typeck_results, expr) {
+ field_accesses.insert(sym);
+ }
+ ControlFlow::<!, _>::Continue(())
+ });
+
+ let span_notes = data
+ .fields()
+ .iter()
+ .filter_map(|field| {
+ if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) {
+ None
+ } else {
+ Some((field.span, "this field is unused"))
+ }
+ })
+ .collect::<Vec<_>>();
+
+ // only lint if there's also at least one direct field access to allow patterns
+ // where one might have a newtype struct and uses fields from the wrapped type
+ if !span_notes.is_empty() && has_direct_field_access {
+ report_lints(cx, item.span, span_notes);
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
+ // is this an `impl Debug for X` block?
+ if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind
+ && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
+ && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
+ // make sure that the self type is either a struct, an enum or a union
+ // this prevents ICEs such as when self is a type parameter or a primitive type
+ // (see #10887, #11063)
+ && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res
+ && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
+ // don't trigger if this impl was derived
+ && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
+ && !item.span.from_expansion()
+ // find `Debug::fmt` function
+ && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt)
+ && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir().impl_item(fmt_item.id)
+ && let body = cx.tcx.hir().body(*body_id)
+ && let ExprKind::Block(block, _) = body.value.kind
+ // inspect `self`
+ && let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs()
+ && let Some(self_adt) = self_ty.ty_adt_def()
+ && let Some(self_def_id) = self_adt.did().as_local()
+ && let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id)
+ // NB: can't call cx.typeck_results() as we are not in a body
+ && let typeck_results = cx.tcx.typeck_body(*body_id)
+ && should_lint(cx, typeck_results, block)
+ {
+ // we intentionally only lint structs, see lint description
+ if let ItemKind::Struct(data, _) = &self_item.kind {
+ check_struct(cx, typeck_results, block, self_ty, item, data);
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 5a4595481..a41d5a9ce 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
match tit_.kind {
hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
hir::TraitItemKind::Fn(..) => {
- if cx.tcx.impl_defaultness(tit.id.owner_id).has_value() {
+ if cx.tcx.defaultness(tit.id.owner_id).has_value() {
// trait method with default body needs inline in case
// an impl is not provided
let desc = "a default trait method";
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 f0be7771b..57ec3a1f1 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
@@ -114,7 +114,7 @@ struct DivergenceVisitor<'a, 'tcx> {
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
match e.kind {
- ExprKind::Closure { .. } => {},
+ ExprKind::Closure(..) | ExprKind::If(..) | ExprKind::Loop(..) => {},
ExprKind::Match(e, arms, _) => {
self.visit_expr(e);
for arm in arms {
@@ -128,6 +128,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
_ => walk_expr(self, e),
}
}
+
fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
}
@@ -136,6 +137,15 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
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),
+ _ => {},
+ },
+ _ => {},
+ },
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
ExprKind::Call(func, _) => {
let typ = self.cx.typeck_results().expr_ty(func);
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
index 349fcd227..439cae812 100644
--- a/src/tools/clippy/clippy_lints/src/module_style.rs
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -2,6 +2,7 @@ use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{FileName, SourceFile, Span, SyntaxContext};
use std::ffi::OsStr;
use std::path::{Component, Path};
@@ -90,7 +91,14 @@ impl EarlyLintPass for ModStyle {
// `{ foo => path/to/foo.rs, .. }
let mut file_map = FxHashMap::default();
for file in files.iter() {
- if let FileName::Real(name) = &file.name && let Some(lp) = name.local_path() {
+ if let FileName::Real(name) = &file.name
+ && let Some(lp) = name.local_path()
+ && file.cnum == LOCAL_CRATE
+ {
+ // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887)
+ // Only check files in the current crate.
+ // Fix false positive that crate dependency in workspace sub directory
+ // is checked unintentionally.
let path = if lp.is_relative() {
lp
} else if let Ok(relative) = lp.strip_prefix(trim_to_src) {
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 2abdfacd2..e6fd65f00 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
@@ -138,7 +138,7 @@ fn collect_unsafe_exprs<'tcx>(
.type_dependent_def_id(expr.hir_id)
.map(|def_id| cx.tcx.fn_sig(def_id))
{
- if sig.0.unsafety() == Unsafety::Unsafe {
+ if sig.skip_binder().unsafety() == Unsafety::Unsafe {
unsafe_ops.push(("unsafe method call occurs here", expr.span));
}
}
diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs
new file mode 100644
index 000000000..4ff1bf7ff
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/needless_else.rs
@@ -0,0 +1,61 @@
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span};
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for empty `else` branches.
+ ///
+ /// ### Why is this bad?
+ /// An empty else branch does nothing and can be removed.
+ ///
+ /// ### Example
+ /// ```rust
+ ///# fn check() -> bool { true }
+ /// if check() {
+ /// println!("Check successful!");
+ /// } else {
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ ///# fn check() -> bool { true }
+ /// if check() {
+ /// println!("Check successful!");
+ /// }
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub NEEDLESS_ELSE,
+ style,
+ "empty else branch"
+}
+declare_lint_pass!(NeedlessElse => [NEEDLESS_ELSE]);
+
+impl EarlyLintPass for NeedlessElse {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind
+ && let ExprKind::Block(block, _) = &else_clause.kind
+ && !expr.span.from_expansion()
+ && !else_clause.span.from_expansion()
+ && block.stmts.is_empty()
+ && let Some(trimmed) = expr.span.trim_start(then_block.span)
+ && let span = trim_span(cx.sess().source_map(), trimmed)
+ && let Some(else_snippet) = snippet_opt(cx, span)
+ // Ignore else blocks that contain comments or #[cfg]s
+ && !else_snippet.contains(['/', '#'])
+ {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_ELSE,
+ span,
+ "this else branch is empty",
+ "you can remove it",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs
new file mode 100644
index 000000000..ad5c3e1dc
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/needless_if.rs
@@ -0,0 +1,75 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher::If, is_from_proc_macro, source::snippet_opt};
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for empty `if` branches with no else branch.
+ ///
+ /// ### Why is this bad?
+ /// It can be entirely omitted, and often the condition too.
+ ///
+ /// ### Known issues
+ /// This will usually only suggest to remove the `if` statement, not the condition. Other lints
+ /// such as `no_effect` will take care of removing the condition if it's unnecessary.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if really_expensive_condition(&i) {}
+ /// if really_expensive_condition_with_side_effects(&mut i) {}
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// // <omitted>
+ /// really_expensive_condition_with_side_effects(&mut i);
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub NEEDLESS_IF,
+ complexity,
+ "checks for empty if branches"
+}
+declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]);
+
+impl LateLintPass<'_> for NeedlessIf {
+ fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) {
+ if let StmtKind::Expr(expr) = stmt.kind
+ && let Some(If {cond, then, r#else: None }) = If::hir(expr)
+ && let ExprKind::Block(block, ..) = then.kind
+ && 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
+ // - empty reptitions in macro expansions
+ // - comments
+ // - #[cfg]'d out code
+ && then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
+ && let Some(cond_snippet) = snippet_opt(cx, cond.span)
+ {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_IF,
+ stmt.span,
+ "this `if` branch is empty",
+ "you can remove it",
+ if cond.can_have_side_effects() || !cx.tcx.hir().attrs(stmt.hir_id).is_empty() {
+ // `{ foo }` or `{ foo } && bar` placed into a statement position would be
+ // interpreted as a block statement, force it to be an expression
+ if cond_snippet.starts_with('{') {
+ format!("({cond_snippet});")
+ } else {
+ format!("{cond_snippet};")
+ }
+ } else {
+ String::new()
+ },
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
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 0bb1775aa..f11d5773d 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
@@ -7,18 +7,17 @@ use clippy_utils::ty::{
use clippy_utils::{get_trait_def_id, is_self, paths};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
-use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind,
};
-use rustc_hir::{HirIdMap, HirIdSet, LangItem};
+use rustc_hir::{HirIdSet, LangItem};
use rustc_hir_typeck::expr_use_visitor as euv;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::{self, TypeVisitableExt};
+use rustc_middle::ty::{self, TypeVisitableExt, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::kw;
@@ -26,7 +25,6 @@ use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
-use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@@ -127,9 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
.filter_map(|pred| {
// Note that we do not want to deal with qualified predicates here.
match pred.kind().no_bound_vars() {
- Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
- Some(pred)
- },
+ Some(ty::ClauseKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred),
_ => None,
}
})
@@ -137,11 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
// Collect moved variables and spans which will need dereferencings from the
// function body.
- let MovedVariablesCtxt {
- moved_vars,
- spans_need_deref,
- ..
- } = {
+ let MovedVariablesCtxt { moved_vars } = {
let mut ctx = MovedVariablesCtxt::default();
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);
@@ -176,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
(
preds.iter().any(|t| cx.tcx.is_diagnostic_item(sym::Borrow, t.def_id())),
!preds.is_empty() && {
- let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_erased, ty);
+ let ty_empty_region = Ty::new_imm_ref(cx.tcx,cx.tcx.lifetimes.re_erased, ty);
preds.iter().all(|t| {
let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
@@ -212,7 +204,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
}
}
- let deref_span = spans_need_deref.get(&canonical_id);
if_chain! {
if is_type_diagnostic_item(cx, ty, sym::Vec);
if let Some(clone_spans) =
@@ -240,16 +231,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
snippet_opt(cx, span)
.map_or(
"change the call to".into(),
- |x| Cow::from(format!("change `{x}` to")),
- )
- .as_ref(),
+ |x| format!("change `{x}` to"),
+ ),
suggestion,
Applicability::Unspecified,
);
}
// cannot be destructured, no need for `*` suggestion
- assert!(deref_span.is_none());
return;
}
}
@@ -270,31 +259,19 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
snippet_opt(cx, span)
.map_or(
"change the call to".into(),
- |x| Cow::from(format!("change `{x}` to"))
- )
- .as_ref(),
+ |x| format!("change `{x}` to")
+ ),
suggestion,
Applicability::Unspecified,
);
}
- assert!(deref_span.is_none());
return;
}
}
- let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
-
- // Suggests adding `*` to dereference the added reference.
- if let Some(deref_span) = deref_span {
- spans.extend(
- deref_span
- .iter()
- .copied()
- .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
- );
- spans.sort_by_key(|&(span, _)| span);
- }
+ let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
+
multispan_sugg(diag, "consider taking a reference instead", spans);
};
@@ -323,9 +300,6 @@ fn requires_exact_signature(attrs: &[Attribute]) -> bool {
#[derive(Default)]
struct MovedVariablesCtxt {
moved_vars: HirIdSet,
- /// Spans which need to be prefixed with `*` for dereferencing the
- /// suggested additional reference.
- spans_need_deref: HirIdMap<FxHashSet<Span>>,
}
impl MovedVariablesCtxt {
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index e3712190e..a4c7da7e4 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,11 +1,16 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::is_lint_allowed;
use clippy_utils::peel_blocks;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::has_drop;
+use clippy_utils::{get_parent_node, is_lint_allowed};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
+use rustc_hir::{
+ is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind,
+ UnsafeSource,
+};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -86,7 +91,43 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
if let StmtKind::Semi(expr) = stmt.kind {
if has_no_effect(cx, expr) {
- span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
+ span_lint_hir_and_then(
+ cx,
+ NO_EFFECT,
+ expr.hir_id,
+ stmt.span,
+ "statement with no effect",
+ |diag| {
+ for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
+ if let Node::Item(item) = parent.1
+ && let ItemKind::Fn(sig, ..) = item.kind
+ && let FnRetTy::Return(ret_ty) = sig.decl.output
+ && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
+ && let [.., final_stmt] = block.stmts
+ && final_stmt.hir_id == stmt.hir_id
+ {
+ let expr_ty = cx.typeck_results().expr_ty(expr);
+ let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty);
+
+ // 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)
+ {
+ ret_ty = true_ret_ty;
+ }
+
+ if ret_ty == expr_ty {
+ diag.span_suggestion(
+ stmt.span.shrink_to_lo(),
+ "did you mean to return it?",
+ "return ",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ },
+ );
return true;
}
} else if let StmtKind::Local(local) = stmt.kind {
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 58590df1f..75f1e9527 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -15,12 +15,14 @@ use rustc_hir::{
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
+use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, InnerSpan, Span};
+use rustc_target::abi::VariantIdx;
+use rustc_middle::mir::interpret::EvalToValTreeResult;
+use rustc_middle::mir::interpret::GlobalId;
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
@@ -141,21 +143,35 @@ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
fn is_value_unfrozen_raw<'tcx>(
cx: &LateContext<'tcx>,
- result: Result<ConstValue<'tcx>, ErrorHandled>,
+ result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
ty: Ty<'tcx>,
) -> bool {
- fn inner<'tcx>(cx: &LateContext<'tcx>, val: mir::ConstantKind<'tcx>) -> bool {
- match val.ty().kind() {
+ 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::Adt(..) | ty::Tuple(..) => {
- let val = cx.tcx.destructure_mir_constant(cx.param_env, val);
- val.fields.iter().any(|field| inner(cx, *field))
+ ty::Array(ty, _) => {
+ val.unwrap_branch().iter().any(|field| inner(cx, *field, ty))
},
+ ty::Adt(def, _) if def.is_union() => false,
+ ty::Adt(def, substs) 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, substs))).any(|(field, ty)| inner(cx, field, ty))
+ }
+ ty::Adt(def, substs) => {
+ val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, *field, ty))
+ }
+ ty::Tuple(tys) => val.unwrap_branch().iter().zip(tys).any(|(field, ty)| inner(cx, *field, ty)),
_ => false,
}
}
@@ -166,15 +182,15 @@ fn is_value_unfrozen_raw<'tcx>(
// 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`).
+ // 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
@@ -184,24 +200,44 @@ fn is_value_unfrozen_raw<'tcx>(
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
err == ErrorHandled::TooGeneric
},
- |val| inner(cx, mir::ConstantKind::from_value(val, ty)),
+ |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 result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id());
+ let def_id = body_id.hir_id.owner.to_def_id();
+ let substs = ty::InternalSubsts::identity_for_item(cx.tcx, def_id);
+ let instance = ty::Instance::new(def_id, substs);
+ 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 substs = cx.typeck_results().node_substs(hir_id);
- let result = cx
- .tcx
- .const_eval_resolve(cx.param_env, mir::UnevaluatedConst::new(def_id, substs), None);
+ let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, substs), 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.substs) {
+ 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),
+ Err(err) => Err(ErrorHandled::Reported(err.into())),
+ }
+}
+
#[derive(Copy, Clone)]
enum Source {
Item { item: Span },
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 6c909e5ed..2d79a5c90 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -241,7 +241,7 @@ impl<'de> Deserialize<'de> for MacroMatcher {
V: de::MapAccess<'de>,
{
let mut name = None;
- let mut brace: Option<&str> = None;
+ let mut brace: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Name => {
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
deleted file mode 100644
index e18064b70..000000000
--- a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::in_constant;
-use rustc_hir::{BinOpKind, Expr};
-use rustc_lint::LateContext;
-
-use super::CMP_NAN;
-
-pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
- if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) {
- span_lint(
- cx,
- CMP_NAN,
- e.span,
- "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
- );
- }
-}
-
-fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- if let Some(value) = constant(cx, cx.typeck_results(), e) {
- match value {
- Constant::F32(num) => num.is_nan(),
- Constant::F64(num) => num.is_nan(),
- _ => false,
- }
- } else {
- false
- }
-}
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 67913f739..78965b7d6 100644
--- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
use rustc_hir::{BinOpKind, Expr};
@@ -35,11 +35,16 @@ pub(crate) fn check<'tcx>(
right: &'tcx Expr<'_>,
) {
if is_useless_with_eq_exprs(op.into()) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
- span_lint(
+ span_lint_and_then(
cx,
EQ_OP,
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() {
+ 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/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
index 15dff126b..f3e0c58a7 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -85,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static
}
}
-fn is_allowed(val: &Constant) -> bool {
+fn is_allowed(val: &Constant<'_>) -> bool {
match val {
&Constant::F32(f) => f == 0.0 || f.is_infinite(),
&Constant::F64(f) => f == 0.0 || f.is_infinite(),
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index d63a836e7..2cf15adda 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -1,7 +1,6 @@
mod absurd_extreme_comparisons;
mod assign_op_pattern;
mod bit_mask;
-mod cmp_nan;
mod cmp_owned;
mod double_comparison;
mod duration_subsec;
@@ -487,31 +486,6 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for comparisons to NaN.
- ///
- /// ### Why is this bad?
- /// NaN does not compare meaningfully to anything – not
- /// even itself – so those comparisons are simply wrong.
- ///
- /// ### Example
- /// ```rust
- /// # let x = 1.0;
- /// if x == f32::NAN { }
- /// ```
- ///
- /// Use instead:
- /// ```rust
- /// # let x = 1.0f32;
- /// if x.is_nan() { }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub CMP_NAN,
- correctness,
- "comparisons to `NAN`, which will always return false, probably not intended"
-}
-
-declare_clippy_lint! {
- /// ### What it does
/// Checks for conversions to owned values just for the sake
/// of a comparison.
///
@@ -775,7 +749,6 @@ impl_lint_pass!(Operators => [
FLOAT_EQUALITY_WITHOUT_ABS,
IDENTITY_OP,
INTEGER_DIVISION,
- CMP_NAN,
CMP_OWNED,
FLOAT_CMP,
FLOAT_CMP_CONST,
@@ -816,7 +789,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
duration_subsec::check(cx, e, op.node, lhs, rhs);
float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
integer_division::check(cx, e, op.node, lhs, rhs);
- cmp_nan::check(cx, e, op.node, lhs, rhs);
cmp_owned::check(cx, op.node, lhs, rhs);
float_cmp::check(cx, e, op.node, lhs, rhs);
modulo_one::check(cx, e, op.node, rhs);
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 aa6d40042..abdccc47f 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
@@ -140,6 +140,9 @@ fn try_get_option_occurrence<'tcx>(
let (as_ref, as_mut) = match &expr.kind {
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
+ _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => {
+ (mutb == Mutability::Not, mutb == Mutability::Mut)
+ }
_ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT),
};
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 0d78c3048..eab725de1 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
@@ -14,7 +14,7 @@ use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::adjustment::{Adjust, PointerCast};
+use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, RegionKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -195,7 +195,7 @@ impl<'tcx> PassByRefOrValue {
.adjustments()
.items()
.flat_map(|(_, a)| a)
- .any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer)))
+ .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer)))
{
continue;
}
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index fc5509361..32213718b 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -5,6 +5,7 @@ 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 hir::LifetimeName;
use if_chain::if_chain;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::def_id::DefId;
@@ -15,11 +16,12 @@ use rustc_hir::{
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
TyKind, Unsafety,
};
+use rustc_hir_analysis::hir_ty_to_ty;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::{self, Binder, Clause, ExistentialPredicate, List, PredicateKind, Ty};
+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;
@@ -166,6 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
cx,
cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(),
sig.decl.inputs,
+ &sig.decl.output,
&[],
)
.filter(|arg| arg.mutability() == Mutability::Not)
@@ -218,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
check_mut_from_ref(cx, sig, Some(body));
let decl = sig.decl;
let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder();
- let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
+ let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
.collect();
let results = check_ptr_arg_usage(cx, body, &lint_args);
@@ -386,11 +389,11 @@ impl<'tcx> DerefTy<'tcx> {
fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
match *self {
Self::Str => cx.tcx.types.str_,
- Self::Path => cx.tcx.mk_adt(
+ Self::Path => Ty::new_adt(cx.tcx,
cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
List::empty(),
),
- Self::Slice(_, ty) => cx.tcx.mk_slice(ty),
+ Self::Slice(_, ty) => Ty::new_slice(cx.tcx,ty),
}
}
@@ -407,29 +410,27 @@ impl<'tcx> DerefTy<'tcx> {
}
}
+#[expect(clippy::too_many_lines)]
fn check_fn_args<'cx, 'tcx: 'cx>(
cx: &'cx LateContext<'tcx>,
tys: &'tcx [Ty<'tcx>],
hir_tys: &'tcx [hir::Ty<'tcx>],
+ ret_ty: &'tcx FnRetTy<'tcx>,
params: &'tcx [Param<'tcx>],
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
tys.iter()
.zip(hir_tys.iter())
.enumerate()
- .filter_map(|(i, (ty, hir_ty))| {
- if_chain! {
- if let ty::Ref(_, ty, mutability) = *ty.kind();
- if let ty::Adt(adt, substs) = *ty.kind();
-
- if let TyKind::Ref(lt, ref ty) = hir_ty.kind;
- if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
-
+ .filter_map(move |(i, (ty, hir_ty))| {
+ if let ty::Ref(_, ty, mutability) = *ty.kind()
+ && let ty::Adt(adt, substs) = *ty.kind()
+ && let TyKind::Ref(lt, ref ty) = hir_ty.kind
+ && let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind
// Check that the name as typed matches the actual name of the type.
// e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
- if let [.., name] = path.segments;
- if cx.tcx.item_name(adt.did()) == name.ident.name;
-
- then {
+ && 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) => (
@@ -454,30 +455,65 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
DerefTy::Path,
),
Some(sym::Cow) if mutability == Mutability::Not => {
- let ty_name = name.args
+ if let Some((lifetime, ty)) = name.args
.and_then(|args| {
- args.args.iter().find_map(|a| match a {
- GenericArg::Type(x) => Some(x),
- _ => None,
- })
+ if let [GenericArg::Lifetime(lifetime), ty] = args.args {
+ return Some((lifetime, ty));
+ }
+ None
})
- .and_then(|arg| snippet_opt(cx, arg.span))
- .unwrap_or_else(|| substs.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,
- );
+ {
+ if !lifetime.is_anonymous()
+ && let FnRetTy::Return(ret_ty) = ret_ty
+ && let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty)
+ && ret_ty
+ .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(|| substs.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,
@@ -495,7 +531,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
},
deref_ty,
});
- }
}
None
})
@@ -697,7 +732,7 @@ fn matches_preds<'tcx>(
ObligationCause::dummy(),
cx.param_env,
cx.tcx
- .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
+ .mk_predicate(Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(
p.with_self_ty(cx.tcx, ty),
)))),
)),
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 5269bbd1f..e3d940ad2 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,19 +1,20 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id,
peel_blocks, peel_blocks_with_stmt,
};
+use clippy_utils::{higher, is_path_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
-use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::declare_tool_lint;
+use rustc_session::impl_lint_pass;
use rustc_span::{sym, symbol::Symbol};
declare_clippy_lint! {
@@ -41,7 +42,16 @@ declare_clippy_lint! {
"checks for expressions that could be replaced by the question mark operator"
}
-declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
+#[derive(Default)]
+pub struct QuestionMark {
+ /// Keeps track of how many try blocks we are in at any point during linting.
+ /// This allows us to answer the question "are we inside of a try block"
+ /// very quickly, without having to walk up the parent chain, by simply checking
+ /// if it is greater than zero.
+ /// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
+ try_block_depth_stack: Vec<u32>,
+}
+impl_lint_pass!(QuestionMark => [QUESTION_MARK]);
enum IfBlockType<'hir> {
/// An `if x.is_xxx() { a } else { b } ` expression.
@@ -68,98 +78,6 @@ enum IfBlockType<'hir> {
),
}
-/// Checks if the given expression on the given context matches the following structure:
-///
-/// ```ignore
-/// if option.is_none() {
-/// return None;
-/// }
-/// ```
-///
-/// ```ignore
-/// if result.is_err() {
-/// return result;
-/// }
-/// ```
-///
-/// If it matches, it will suggest to use the question mark operator instead
-fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
- if_chain! {
- if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
- if !is_else_clause(cx.tcx, expr);
- if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
- let caller_ty = cx.typeck_results().expr_ty(caller);
- let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
- if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
- then {
- let mut applicability = Applicability::MachineApplicable;
- let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
- let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) &&
- !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
- let sugg = if let Some(else_inner) = r#else {
- if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
- format!("Some({receiver_str}?)")
- } else {
- return;
- }
- } else {
- format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
- };
-
- span_lint_and_sugg(
- cx,
- QUESTION_MARK,
- expr.span,
- "this block may be rewritten with the `?` operator",
- "replace it with",
- sugg,
- applicability,
- );
- }
- }
-}
-
-fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
- if_chain! {
- if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
- if !is_else_clause(cx.tcx, expr);
- if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind;
- if ddpos.as_opt_usize().is_none();
- if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
- let caller_ty = cx.typeck_results().expr_ty(let_expr);
- let if_block = IfBlockType::IfLet(
- cx.qpath_res(path1, let_pat.hir_id),
- caller_ty,
- ident.name,
- let_expr,
- if_then,
- if_else
- );
- if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
- || is_early_return(sym::Result, cx, &if_block);
- if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
- then {
- let mut applicability = Applicability::MachineApplicable;
- let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
- let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
- let sugg = format!(
- "{receiver_str}{}?{}",
- if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
- if requires_semi { ";" } else { "" }
- );
- span_lint_and_sugg(
- cx,
- QUESTION_MARK,
- expr.span,
- "this block may be rewritten with the `?` operator",
- "replace it with",
- sugg,
- applicability,
- );
- }
- }
-}
-
fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
match *if_block {
IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
@@ -230,11 +148,147 @@ fn expr_return_none_or_err(
}
}
+impl QuestionMark {
+ fn inside_try_block(&self) -> bool {
+ self.try_block_depth_stack.last() > Some(&0)
+ }
+
+ /// Checks if the given expression on the given context matches the following structure:
+ ///
+ /// ```ignore
+ /// if option.is_none() {
+ /// return None;
+ /// }
+ /// ```
+ ///
+ /// ```ignore
+ /// if result.is_err() {
+ /// return result;
+ /// }
+ /// ```
+ ///
+ /// If it matches, it will suggest to use the question mark operator instead
+ fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ if_chain! {
+ if !self.inside_try_block();
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+ if !is_else_clause(cx.tcx, expr);
+ if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
+ let caller_ty = cx.typeck_results().expr_ty(caller);
+ let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
+ if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
+ let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) &&
+ !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
+ let sugg = if let Some(else_inner) = r#else {
+ if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
+ format!("Some({receiver_str}?)")
+ } else {
+ return;
+ }
+ } else {
+ format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
+ };
+
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this block may be rewritten with the `?` operator",
+ "replace it with",
+ sugg,
+ applicability,
+ );
+ }
+ }
+ }
+
+ fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ if_chain! {
+ if !self.inside_try_block();
+ if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
+ if !is_else_clause(cx.tcx, expr);
+ if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind;
+ if ddpos.as_opt_usize().is_none();
+ if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
+ let caller_ty = cx.typeck_results().expr_ty(let_expr);
+ let if_block = IfBlockType::IfLet(
+ cx.qpath_res(path1, let_pat.hir_id),
+ caller_ty,
+ ident.name,
+ let_expr,
+ if_then,
+ if_else
+ );
+ if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
+ || is_early_return(sym::Result, cx, &if_block);
+ if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
+ let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
+ let sugg = format!(
+ "{receiver_str}{}?{}",
+ if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
+ if requires_semi { ";" } else { "" }
+ );
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this block may be rewritten with the `?` operator",
+ "replace it with",
+ sugg,
+ applicability,
+ );
+ }
+ }
+ }
+}
+
+fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
+ if let Some(expr) = bl.expr
+ && let rustc_hir::ExprKind::Call(callee, _) = expr.kind
+ {
+ is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput)
+ } else {
+ false
+ }
+}
+
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !in_constant(cx, expr.hir_id) {
- check_is_none_or_err_and_early_return(cx, expr);
- check_if_let_some_or_err_and_early_return(cx, expr);
+ self.check_is_none_or_err_and_early_return(cx, expr);
+ self.check_if_let_some_or_err_and_early_return(cx, expr);
+ }
+ }
+
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) {
+ if is_try_block(cx, block) {
+ *self
+ .try_block_depth_stack
+ .last_mut()
+ .expect("blocks are always part of bodies and must have a depth") += 1;
+ }
+ }
+
+ fn check_body(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) {
+ self.try_block_depth_stack.push(0);
+ }
+
+ fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) {
+ self.try_block_depth_stack.pop();
+ }
+
+ fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) {
+ if is_try_block(cx, block) {
+ *self
+ .try_block_depth_stack
+ .last_mut()
+ .expect("blocks are always part of bodies and must have a depth") -= 1;
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index dd7ded491..d2018aba9 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -296,8 +296,8 @@ fn check_possible_range_contains(
}
}
-struct RangeBounds<'a> {
- val: Constant,
+struct RangeBounds<'a, 'tcx> {
+ val: Constant<'tcx>,
expr: &'a Expr<'a>,
id: HirId,
name_span: Span,
@@ -309,7 +309,7 @@ struct RangeBounds<'a> {
// Takes a binary expression such as x <= 2 as input
// Breaks apart into various pieces, such as the value of the number,
// hir id of the variable, and direction/inclusiveness of the operator
-fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
+fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a, 'tcx>> {
if let ExprKind::Binary(ref op, l, r) = ex.kind {
let (inclusive, ordering) = match op.node {
BinOpKind::Gt => (false, Ordering::Greater),
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
new file mode 100644
index 000000000..f45bb1ef3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -0,0 +1,143 @@
+use std::{iter::once, ops::ControlFlow};
+
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
+use rustc_ast::{
+ ast::{Expr, ExprKind},
+ token::LitKind,
+};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for raw string literals where a string literal can be used instead.
+ ///
+ /// ### Why is this bad?
+ /// It's just unnecessary, but there are many cases where using a raw string literal is more
+ /// idiomatic than a string literal, so it's opt-in.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let r = r"Hello, world!";
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let r = "Hello, world!";
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub NEEDLESS_RAW_STRINGS,
+ restriction,
+ "suggests using a string literal when a raw string literal is unnecessary"
+}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for raw string literals with an unnecessary amount of hashes around them.
+ ///
+ /// ### Why is this bad?
+ /// It's just unnecessary, and makes it look like there's more escaping needed than is actually
+ /// necessary.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let r = r###"Hello, "world"!"###;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let r = r#"Hello, "world"!"#;
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub NEEDLESS_RAW_STRING_HASHES,
+ style,
+ "suggests reducing the number of hashes around a raw string literal"
+}
+impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRINGS, NEEDLESS_RAW_STRING_HASHES]);
+
+pub struct RawStrings {
+ pub needless_raw_string_hashes_allow_one: bool,
+}
+
+impl EarlyLintPass for RawStrings {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if !in_external_macro(cx.sess(), expr.span)
+ && let ExprKind::Lit(lit) = expr.kind
+ && let LitKind::StrRaw(max) | LitKind::ByteStrRaw(max) | LitKind::CStrRaw(max) = lit.kind
+ {
+ let str = lit.symbol.as_str();
+ let prefix = match lit.kind {
+ LitKind::StrRaw(..) => "r",
+ LitKind::ByteStrRaw(..) => "br",
+ LitKind::CStrRaw(..) => "cr",
+ _ => unreachable!(),
+ };
+ if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
+ return;
+ }
+
+ if !str.contains(['\\', '"']) {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RAW_STRINGS,
+ expr.span,
+ "unnecessary raw string literal",
+ "try",
+ format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
+ Applicability::MachineApplicable,
+ );
+
+ return;
+ }
+
+ let req = {
+ let mut following_quote = false;
+ let mut req = 0;
+ // `once` so a raw string ending in hashes is still checked
+ let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
+ match b {
+ b'"' => (following_quote, req) = (true, 1),
+ // I'm a bit surprised the compiler didn't optimize this out, there's no
+ // branch but it still ends up doing an unnecessary comparison, it's:
+ // - cmp r9b,1h
+ // - sbb cl,-1h
+ // which will add 1 if it's true. With this change, it becomes:
+ // - add cl,r9b
+ // isn't that so much nicer?
+ b'#' => req += u8::from(following_quote),
+ _ => {
+ if following_quote {
+ following_quote = false;
+
+ if req == max {
+ return ControlFlow::Break(req);
+ }
+
+ return ControlFlow::Continue(acc.max(req));
+ }
+ },
+ }
+
+ ControlFlow::Continue(acc)
+ });
+
+ match num {
+ ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
+ }
+ };
+
+ if req < max {
+ let hashes = "#".repeat(req as usize);
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RAW_STRING_HASHES,
+ expr.span,
+ "unnecessary hashes around raw string literal",
+ "try",
+ format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
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 a0f831764..05e52e6b3 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -35,7 +35,7 @@ declare_clippy_lint! {
/// };
/// let fut = f;
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.70.0"]
pub REDUNDANT_ASYNC_BLOCK,
complexity,
"`async { future.await }` can be replaced by `future`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index 685d738cb..e36adef55 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -320,8 +320,6 @@ fn base_local_and_movability<'tcx>(
mir: &mir::Body<'tcx>,
place: mir::Place<'tcx>,
) -> (mir::Local, CannotMoveOut) {
- use rustc_middle::mir::PlaceRef;
-
// Dereference. You cannot move things out from a borrowed value.
let mut deref = false;
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
@@ -330,17 +328,14 @@ fn base_local_and_movability<'tcx>(
// underlying type implements Copy
let mut slice = false;
- let PlaceRef { local, mut projection } = place.as_ref();
- while let [base @ .., elem] = projection {
- projection = base;
+ for (base, elem) in place.as_ref().iter_projections() {
+ let base_ty = base.ty(&mir.local_decls, cx.tcx).ty;
deref |= matches!(elem, mir::ProjectionElem::Deref);
- field |= matches!(elem, mir::ProjectionElem::Field(..))
- && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
- slice |= matches!(elem, mir::ProjectionElem::Index(..))
- && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
+ field |= matches!(elem, mir::ProjectionElem::Field(..)) && has_drop(cx, base_ty);
+ slice |= matches!(elem, mir::ProjectionElem::Index(..)) && !is_copy(cx, base_ty);
}
- (local, deref || field || slice)
+ (place.local, deref || field || slice)
}
#[derive(Default)]
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 2a42e7348..b6ce4ebc2 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -1,14 +1,14 @@
+use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::get_parent_expr;
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_ast::visit as ast_visit;
-use rustc_ast::visit::Visitor as AstVisitor;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit as hir_visit;
use rustc_hir::intravisit::Visitor as HirVisitor;
-use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_hir::intravisit::Visitor;
+use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -51,59 +51,136 @@ impl ReturnVisitor {
}
}
-impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor {
- fn visit_expr(&mut self, ex: &'ast ast::Expr) {
- if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind {
+impl<'tcx> Visitor<'tcx> for ReturnVisitor {
+ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+ if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) = ex.kind {
self.found_return = true;
+ } else {
+ hir_visit::walk_expr(self, ex);
}
+ }
+}
- ast_visit::walk_expr(self, ex);
+/// Checks if the body is owned by an async closure
+fn is_async_closure(body: &hir::Body<'_>) -> bool {
+ if let hir::ExprKind::Closure(closure) = body.value.kind
+ && let [resume_ty] = closure.fn_decl.inputs
+ && let hir::TyKind::Path(hir::QPath::LangItem(hir::LangItem::ResumeTy, ..)) = resume_ty.kind
+ {
+ true
+ } else {
+ false
}
}
-impl EarlyLintPass for RedundantClosureCall {
- fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+/// Tries to find the innermost closure:
+/// ```rust,ignore
+/// (|| || || || 42)()()()()
+/// ^^^^^^^^^^^^^^ given this nested closure expression
+/// ^^^^^ we want to return this closure
+/// ```
+/// It also has a parameter for how many steps to go in at most, so as to
+/// not take more closures than there are calls.
+fn find_innermost_closure<'tcx>(
+ cx: &LateContext<'tcx>,
+ mut expr: &'tcx hir::Expr<'tcx>,
+ mut steps: usize,
+) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> {
+ let mut data = None;
+
+ while let hir::ExprKind::Closure(closure) = expr.kind
+ && let body = cx.tcx.hir().body(closure.body)
+ && {
+ let mut visitor = ReturnVisitor::new();
+ visitor.visit_expr(body.value);
+ !visitor.found_return
+ }
+ && steps > 0
+ {
+ expr = body.value;
+ data = Some((body.value, closure.fn_decl, if is_async_closure(body) {
+ hir::IsAsync::Async
+ } else {
+ hir::IsAsync::NotAsync
+ }));
+ steps -= 1;
+ }
+
+ data
+}
+
+/// "Walks up" the chain of calls to find the outermost call expression, and returns the depth:
+/// ```rust,ignore
+/// (|| || || 3)()()()
+/// ^^ this is the call expression we were given
+/// ^^ this is what we want to return (and the depth is 3)
+/// ```
+fn get_parent_call_exprs<'tcx>(
+ cx: &LateContext<'tcx>,
+ mut expr: &'tcx hir::Expr<'tcx>,
+) -> (&'tcx hir::Expr<'tcx>, usize) {
+ let mut depth = 1;
+ while let Some(parent) = get_parent_expr(cx, expr)
+ && let hir::ExprKind::Call(recv, _) = parent.kind
+ && expr.span == recv.span
+ {
+ expr = parent;
+ depth += 1;
+ }
+ (expr, depth)
+}
+
+impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
- if_chain! {
- if let ast::ExprKind::Call(ref paren, _) = expr.kind;
- if let ast::ExprKind::Paren(ref closure) = paren.kind;
- if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind;
- then {
- let mut visitor = ReturnVisitor::new();
- visitor.visit_expr(body);
- if !visitor.found_return {
- span_lint_and_then(
- cx,
- REDUNDANT_CLOSURE_CALL,
- expr.span,
- "try not to call a closure in the expression where it is declared",
- |diag| {
- if fn_decl.inputs.is_empty() {
- let mut app = Applicability::MachineApplicable;
- let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
-
- if asyncness.is_async() {
- // `async x` is a syntax error, so it becomes `async { x }`
- if !matches!(body.kind, ast::ExprKind::Block(_, _)) {
- hint = hint.blockify();
- }
-
- hint = hint.asyncify();
- }
-
- diag.span_suggestion(expr.span, "try doing something like", hint.to_string(), app);
+
+ if let hir::ExprKind::Call(recv, _) = expr.kind
+ // don't lint if the receiver is a call, too.
+ // we do this in order to prevent linting multiple times; consider:
+ // `(|| || 1)()()`
+ // ^^ we only want to lint for this call (but we walk up the calls to consider both calls).
+ // 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)
+ {
+ span_lint_and_then(
+ cx,
+ REDUNDANT_CLOSURE_CALL,
+ full_expr.span,
+ "try not to call a closure in the expression where it is declared",
+ |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);
+
+ if generator_kind.is_async()
+ && let hir::ExprKind::Closure(closure) = body.kind
+ {
+ let async_closure_body = cx.tcx.hir().body(closure.body);
+
+ // `async x` is a syntax error, so it becomes `async { x }`
+ if !matches!(async_closure_body.value.kind, hir::ExprKind::Block(_, _)) {
+ hint = hint.blockify();
}
- },
- );
+
+ hint = hint.asyncify();
+ }
+
+ diag.span_suggestion(
+ full_expr.span,
+ "try doing something like",
+ hint.maybe_par(),
+ applicability
+ );
+ }
}
- }
+ );
}
}
-}
-impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
fn count_closure_usage<'tcx>(
cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 2fdd775ad..c70ce83a9 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -7,6 +7,7 @@ use rustc_ast::util::parser::PREC_PREFIX;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_middle::ty::Ty;
use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::subst::GenericArg;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -134,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
} else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(&[GenericArg::from(indexed_ty)])),
+ Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_substs(&[GenericArg::from(indexed_ty)])),
) {
if deref_ty == expr_ty {
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
new file mode 100644
index 000000000..3e963d798
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
@@ -0,0 +1,213 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_lint_allowed;
+use rustc_ast::LitKind;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns about needless / redundant type annotations.
+ ///
+ /// ### Why is this bad?
+ /// Code without type annotations is shorter and in most cases
+ /// more idiomatic and easier to modify.
+ ///
+ /// ### Limitations
+ /// This lint doesn't support:
+ ///
+ /// - Generics
+ /// - Refs returned from anything else than a `MethodCall`
+ /// - Complex types (tuples, arrays, etc...)
+ /// - `Path` to anything else than a primitive type.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let foo: String = String::new();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let foo = String::new();
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub REDUNDANT_TYPE_ANNOTATIONS,
+ restriction,
+ "warns about needless / redundant type annotations."
+}
+declare_lint_pass!(RedundantTypeAnnotations => [REDUNDANT_TYPE_ANNOTATIONS]);
+
+fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, func_return_type: Ty<'tcx>) -> bool {
+ // type annotation is primitive
+ if let hir::def::Res::PrimTy(primty) = ty_resolved_path
+ && func_return_type.is_primitive()
+ && let Some(func_return_type_sym) = func_return_type.primitive_symbol()
+ {
+ return primty.name() == func_return_type_sym;
+ }
+
+ // type annotation is a non generic type
+ if let hir::def::Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, defid) = ty_resolved_path
+ && let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars()
+ {
+ return annotation_ty == func_return_type;
+ }
+
+ false
+}
+
+fn func_hir_id_to_func_ty<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::hir_id::HirId) -> Option<Ty<'tcx>> {
+ if let Some((defkind, func_defid)) = cx.typeck_results().type_dependent_def(hir_id)
+ && defkind == hir::def::DefKind::AssocFn
+ && let Some(init_ty) = cx.tcx.type_of(func_defid).no_bound_vars()
+ {
+ Some(init_ty)
+ } else {
+ None
+ }
+}
+
+fn func_ty_to_return_type<'tcx>(cx: &LateContext<'tcx>, func_ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+ if func_ty.is_fn() {
+ Some(func_ty.fn_sig(cx.tcx).output().skip_binder())
+ } else {
+ None
+ }
+}
+
+/// Extracts the fn Ty, e.g. `fn() -> std::string::String {f}`
+fn extract_fn_ty<'tcx>(
+ cx: &LateContext<'tcx>,
+ call: &hir::Expr<'tcx>,
+ func_return_path: &hir::QPath<'tcx>,
+) -> Option<Ty<'tcx>> {
+ match func_return_path {
+ // let a: String = f(); where f: fn f() -> String
+ hir::QPath::Resolved(_, resolved_path) => {
+ if let hir::def::Res::Def(_, defid) = resolved_path.res
+ && let Some(middle_ty_init) = cx.tcx.type_of(defid).no_bound_vars()
+ {
+ Some(middle_ty_init)
+ } else {
+ None
+ }
+ },
+ // Associated functions like
+ // let a: String = String::new();
+ // let a: String = String::get_string();
+ hir::QPath::TypeRelative(..) => func_hir_id_to_func_ty(cx, call.hir_id),
+ hir::QPath::LangItem(..) => None,
+ }
+}
+
+fn is_redundant_in_func_call<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty_resolved_path: hir::def::Res,
+ call: &hir::Expr<'tcx>,
+) -> bool {
+ if let hir::ExprKind::Path(init_path) = &call.kind {
+ let func_type = extract_fn_ty(cx, call, init_path);
+
+ if let Some(func_type) = func_type
+ && let Some(init_return_type) = func_ty_to_return_type(cx, func_type)
+ {
+ return is_same_type(cx, ty_resolved_path, init_return_type);
+ }
+ }
+
+ false
+}
+
+fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> {
+ if let hir::TyKind::Path(ty_path) = ty_kind
+ && let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
+ && let hir::def::Res::PrimTy(primty) = resolved_path_ty.res
+ {
+ Some(primty)
+ } else {
+ None
+ }
+}
+
+impl LateLintPass<'_> for RedundantTypeAnnotations {
+ fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) {
+ if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id)
+ // type annotation part
+ && !local.span.from_expansion()
+ && let Some(ty) = &local.ty
+
+ // initialization part
+ && let Some(init) = local.init
+ {
+ match &init.kind {
+ // When the initialization is a call to a function
+ 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) {
+ span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
+ }
+ },
+ hir::ExprKind::MethodCall(_, _, _, _) => {
+ let mut is_ref = false;
+ let mut ty_kind = &ty.kind;
+
+ // If the annotation is a ref we "peel" it
+ if let hir::TyKind::Ref(_, mut_ty) = &ty.kind {
+ is_ref = true;
+ ty_kind = &mut_ty.ty.kind;
+ }
+
+ if let hir::TyKind::Path(ty_path) = ty_kind
+ && 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
+ })
+ {
+ span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
+ }
+ },
+ // When the initialization is a path for example u32::MAX
+ 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");
+ }
+ },
+ hir::ExprKind::Lit(init_lit) => {
+ match init_lit.node {
+ // In these cases the annotation is redundant
+ LitKind::Str(..)
+ | LitKind::ByteStr(..)
+ | LitKind::Byte(..)
+ | LitKind::Char(..)
+ | LitKind::Bool(..)
+ | LitKind::CStr(..) => {
+ span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
+ },
+ LitKind::Int(..) | LitKind::Float(..) => {
+ // If the initialization value is a suffixed literal we lint
+ if init_lit.node.is_suffixed() {
+ span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
+ }
+ },
+ LitKind::Err => (),
+ }
+ }
+ _ => ()
+ }
+ };
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index ef19c6f46..674f8bf4c 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -177,7 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
}
fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
- let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build();
+ let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(utf8).build();
if let ExprKind::Lit(lit) = expr.kind {
if let LitKind::Str(ref r, style) = lit.node {
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index b0db56bb4..44e7cbfba 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -31,7 +31,9 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::stutter", "clippy::module_name_repetitions"),
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
("clippy::zero_width_space", "clippy::invisible_characters"),
+ ("clippy::cast_ref_to_mut", "invalid_reference_casting"),
("clippy::clone_double_ref", "suspicious_double_ref_op"),
+ ("clippy::cmp_nan", "invalid_nan_comparisons"),
("clippy::drop_bounds", "drop_bounds"),
("clippy::drop_copy", "dropping_copy_types"),
("clippy::drop_ref", "dropping_references"),
@@ -43,11 +45,13 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::into_iter_on_array", "array_into_iter"),
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
("clippy::invalid_ref", "invalid_value"),
+ ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"),
("clippy::let_underscore_drop", "let_underscore_drop"),
("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
("clippy::panic_params", "non_fmt_panics"),
("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+ ("clippy::undropped_manually_drops", "undropped_manually_drops"),
("clippy::unknown_clippy_lints", "unknown_lints"),
("clippy::unused_label", "unused_labels"),
];
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index df126d761..958351ad8 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -25,6 +25,12 @@ declare_clippy_lint! {
/// It is just extraneous code. Remove it to make your code
/// more rusty.
///
+ /// ### Known problems
+ /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead
+ /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612).
+ /// This could become relevant if the code is later changed to use the code that would have been
+ /// bound without first assigning it to a let-binding.
+ ///
/// ### Example
/// ```rust
/// fn foo() -> String {
@@ -70,7 +76,7 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}
-#[derive(PartialEq, Eq, Clone)]
+#[derive(PartialEq, Eq)]
enum RetReplacement<'tcx> {
Empty,
Block,
@@ -80,7 +86,7 @@ enum RetReplacement<'tcx> {
}
impl<'tcx> RetReplacement<'tcx> {
- fn sugg_help(self) -> &'static str {
+ fn sugg_help(&self) -> &'static str {
match self {
Self::Empty | Self::Expr(..) => "remove `return`",
Self::Block => "replace `return` with an empty block",
@@ -88,10 +94,11 @@ impl<'tcx> RetReplacement<'tcx> {
Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
}
}
- fn applicability(&self) -> Option<Applicability> {
+
+ fn applicability(&self) -> Applicability {
match self {
- Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap),
- _ => None,
+ Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap,
+ _ => Applicability::MachineApplicable,
}
}
}
@@ -271,7 +278,7 @@ fn check_final_expr<'tcx>(
return;
}
- emit_return_lint(cx, ret_span, semi_spans, replacement);
+ emit_return_lint(cx, ret_span, semi_spans, &replacement);
},
ExprKind::If(_, then, else_clause_opt) => {
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@@ -285,7 +292,7 @@ fn check_final_expr<'tcx>(
// (except for unit type functions) so we don't match it
ExprKind::Match(_, arms, MatchSource::Normal) => {
let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
- for arm in arms.iter() {
+ for arm in *arms {
check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
}
},
@@ -306,20 +313,17 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
contains_if(expr, false)
}
-fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement<'_>) {
+fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: &RetReplacement<'_>) {
if ret_span.from_expansion() {
return;
}
- let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
- let return_replacement = replacement.to_string();
- let sugg_help = replacement.sugg_help();
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
- diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
- // for each parent statement, we need to remove the semicolon
- for semi_stmt_span in semi_spans {
- diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability);
- }
+ let suggestions = std::iter::once((ret_span, replacement.to_string()))
+ .chain(semi_spans.into_iter().map(|span| (span, String::new())))
+ .collect();
+
+ diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
});
}
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 b930b2c8d..fffa8a380 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -1,10 +1,10 @@
use clippy_utils::{
diagnostics::span_lint_and_then,
- get_attr,
+ expr_or_init, get_attr, path_to_local,
source::{indent_of, snippet},
};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_errors::Applicability;
use rustc_hir::{
self as hir,
intravisit::{walk_expr, Visitor},
@@ -13,6 +13,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::Ident, Span, DUMMY_SP};
+use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@@ -56,255 +57,102 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
#[derive(Default)]
pub struct SignificantDropTightening<'tcx> {
+ apas: FxIndexMap<hir::HirId, AuxParamsAttr>,
/// Auxiliary structure used to avoid having to verify the same type multiple times.
seen_types: FxHashSet<Ty<'tcx>>,
type_cache: FxHashMap<Ty<'tcx>, bool>,
}
-impl<'tcx> SignificantDropTightening<'tcx> {
- /// Unifies the statements of a block with its return expression.
- fn all_block_stmts<'ret, 'rslt, 'stmts>(
- block_stmts: &'stmts [hir::Stmt<'tcx>],
- dummy_ret_stmt: Option<&'ret hir::Stmt<'tcx>>,
- ) -> impl Iterator<Item = &'rslt hir::Stmt<'tcx>>
- where
- 'ret: 'rslt,
- 'stmts: 'rslt,
- {
- block_stmts.iter().chain(dummy_ret_stmt)
- }
-
- /// Searches for at least one statement that could slow down the release of a significant drop.
- fn at_least_one_stmt_is_expensive<'stmt>(stmts: impl Iterator<Item = &'stmt hir::Stmt<'tcx>>) -> bool
- where
- 'tcx: 'stmt,
- {
- for stmt in stmts {
- match stmt.kind {
- hir::StmtKind::Expr(expr) if let hir::ExprKind::Path(_) = expr.kind => {}
- hir::StmtKind::Local(local) if let Some(expr) = local.init
- && let hir::ExprKind::Path(_) = expr.kind => {},
- _ => return true
- };
- }
- false
- }
-
- /// Verifies if the expression is of type `drop(some_lock_path)` to assert that the temporary
- /// is already being dropped before the end of its scope.
- fn has_drop(expr: &'tcx hir::Expr<'_>, init_bind_ident: Ident) -> bool {
- if let hir::ExprKind::Call(fun, args) = expr.kind
- && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
- && let [fun_ident, ..] = fun_path.segments
- && fun_ident.ident.name == rustc_span::sym::drop
- && let [first_arg, ..] = args
- && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind
- && let [first_arg_ps, .. ] = arg_path.segments
- {
- first_arg_ps.ident == init_bind_ident
- }
- else {
- false
- }
- }
-
- /// Tries to find types marked with `#[has_significant_drop]` of an expression `expr` that is
- /// originated from `stmt` and then performs common logic on `sdap`.
- fn modify_sdap_if_sig_drop_exists(
+impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
+ fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- idx: usize,
- sdap: &mut SigDropAuxParams,
- stmt: &hir::Stmt<'_>,
- cb: impl Fn(&mut SigDropAuxParams),
+ _: hir::intravisit::FnKind<'_>,
+ _: &hir::FnDecl<'_>,
+ body: &'tcx hir::Body<'_>,
+ _: Span,
+ _: hir::def_id::LocalDefId,
) {
- let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types, &mut self.type_cache);
- sig_drop_finder.visit_expr(expr);
- if sig_drop_finder.has_sig_drop {
- cb(sdap);
- if sdap.number_of_stmts > 0 {
- sdap.last_use_stmt_idx = idx;
- sdap.last_use_stmt_span = stmt.span;
- if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind {
- sdap.last_use_method_span = span;
- }
+ self.apas.clear();
+ let initial_dummy_stmt = dummy_stmt_expr(body.value);
+ let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt);
+ StmtsChecker::new(&mut ap, cx, &mut self.seen_types, &mut self.type_cache).visit_body(body);
+ for apa in ap.apas.values() {
+ if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr {
+ continue;
}
- sdap.number_of_stmts = sdap.number_of_stmts.wrapping_add(1);
- }
- }
-
- /// Shows generic overall messages as well as specialized messages depending on the usage.
- fn set_suggestions(cx: &LateContext<'tcx>, block_span: Span, diag: &mut Diagnostic, sdap: &SigDropAuxParams) {
- match sdap.number_of_stmts {
- 0 | 1 => {},
- 2 => {
- let indent = " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0));
- let init_method = snippet(cx, sdap.init_method_span, "..");
- let usage_method = snippet(cx, sdap.last_use_method_span, "..");
- let stmt = if let Some(last_use_bind_span) = sdap.last_use_bind_span {
- format!(
- "\n{indent}let {} = {init_method}.{usage_method};",
- snippet(cx, last_use_bind_span, ".."),
- )
- } else {
- format!("\n{indent}{init_method}.{usage_method};")
- };
- diag.span_suggestion_verbose(
- sdap.init_stmt_span,
- "merge the temporary construction with its single usage",
- stmt,
- Applicability::MaybeIncorrect,
- );
- diag.span_suggestion(
- sdap.last_use_stmt_span,
- "remove separated single usage",
- "",
- Applicability::MaybeIncorrect,
- );
- },
- _ => {
- diag.span_suggestion(
- sdap.last_use_stmt_span.shrink_to_hi(),
- "drop the temporary after the end of its last usage",
- format!(
- "\n{}drop({});",
- " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0)),
- sdap.init_bind_ident
- ),
- Applicability::MaybeIncorrect,
- );
- },
- }
- diag.note("this might lead to unnecessary resource contention");
- diag.span_label(
- block_span,
- format!(
- "temporary `{}` is currently being dropped at the end of its contained scope",
- sdap.init_bind_ident
- ),
- );
- }
-}
-
-impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
- fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
- let dummy_ret_stmt = block.expr.map(|expr| hir::Stmt {
- hir_id: hir::HirId::INVALID,
- kind: hir::StmtKind::Expr(expr),
- span: DUMMY_SP,
- });
- let mut sdap = SigDropAuxParams::default();
- for (idx, stmt) in Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).enumerate() {
- match stmt.kind {
- hir::StmtKind::Expr(expr) => self.modify_sdap_if_sig_drop_exists(
- cx,
- expr,
- idx,
- &mut sdap,
- stmt,
- |_| {}
- ),
- hir::StmtKind::Local(local) if let Some(expr) = local.init => self.modify_sdap_if_sig_drop_exists(
- cx,
- expr,
- idx,
- &mut sdap,
- stmt,
- |local_sdap| {
- if local_sdap.number_of_stmts == 0 {
- if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
- local_sdap.init_bind_ident = ident;
- }
- if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr.kind {
- local_sdap.init_method_span = local_expr.span.to(span);
- }
- local_sdap.init_stmt_span = stmt.span;
- }
- else if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
- local_sdap.last_use_bind_span = Some(ident.span);
- }
- }
- ),
- hir::StmtKind::Semi(expr) => {
- if Self::has_drop(expr, sdap.init_bind_ident) {
- return;
- }
- self.modify_sdap_if_sig_drop_exists(cx, expr, idx, &mut sdap, stmt, |_| {});
- },
- _ => {}
- };
- }
-
- let idx = sdap.last_use_stmt_idx.wrapping_add(1);
- let stmts_after_last_use = Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).skip(idx);
- if sdap.number_of_stmts > 1 && Self::at_least_one_stmt_is_expensive(stmts_after_last_use) {
span_lint_and_then(
cx,
SIGNIFICANT_DROP_TIGHTENING,
- sdap.init_bind_ident.span,
+ apa.first_bind_ident.span,
"temporary with significant `Drop` can be early dropped",
|diag| {
- Self::set_suggestions(cx, block.span, diag, &sdap);
+ match apa.counter {
+ 0 | 1 => {},
+ 2 => {
+ let indent = " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0));
+ let init_method = snippet(cx, apa.first_method_span, "..");
+ let usage_method = snippet(cx, apa.last_method_span, "..");
+ let stmt = if apa.last_bind_ident == Ident::empty() {
+ format!("\n{indent}{init_method}.{usage_method};")
+ } else {
+ format!(
+ "\n{indent}let {} = {init_method}.{usage_method};",
+ snippet(cx, apa.last_bind_ident.span, ".."),
+ )
+ };
+ diag.span_suggestion_verbose(
+ apa.first_stmt_span,
+ "merge the temporary construction with its single usage",
+ stmt,
+ Applicability::MaybeIncorrect,
+ );
+ diag.span_suggestion(
+ apa.last_stmt_span,
+ "remove separated single usage",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ },
+ _ => {
+ diag.span_suggestion(
+ apa.last_stmt_span.shrink_to_hi(),
+ "drop the temporary after the end of its last usage",
+ format!(
+ "\n{}drop({});",
+ " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)),
+ apa.first_bind_ident
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ }
+ diag.note("this might lead to unnecessary resource contention");
+ diag.span_label(
+ apa.first_block_span,
+ format!(
+ "temporary `{}` is currently being dropped at the end of its contained scope",
+ apa.first_bind_ident
+ ),
+ );
},
);
}
}
}
-/// Auxiliary parameters used on each block check.
-struct SigDropAuxParams {
- /// The binding or variable that references the initial construction of the type marked with
- /// `#[has_significant_drop]`.
- init_bind_ident: Ident,
- /// Similar to `init_bind_ident` but encompasses the right-hand method call.
- init_method_span: Span,
- /// Similar to `init_bind_ident` but encompasses the whole contained statement.
- init_stmt_span: Span,
-
- /// The last visited binding or variable span within a block that had any referenced inner type
- /// marked with `#[has_significant_drop]`.
- last_use_bind_span: Option<Span>,
- /// Index of the last visited statement within a block that had any referenced inner type
- /// marked with `#[has_significant_drop]`.
- last_use_stmt_idx: usize,
- /// Similar to `last_use_bind_span` but encompasses the whole contained statement.
- last_use_stmt_span: Span,
- /// Similar to `last_use_bind_span` but encompasses the right-hand method call.
- last_use_method_span: Span,
-
- /// Total number of statements within a block that have any referenced inner type marked with
- /// `#[has_significant_drop]`.
- number_of_stmts: usize,
-}
-
-impl Default for SigDropAuxParams {
- fn default() -> Self {
- Self {
- init_bind_ident: Ident::empty(),
- init_method_span: DUMMY_SP,
- init_stmt_span: DUMMY_SP,
- last_use_bind_span: None,
- last_use_method_span: DUMMY_SP,
- last_use_stmt_idx: 0,
- last_use_stmt_span: DUMMY_SP,
- number_of_stmts: 0,
- }
- }
-}
-
-/// Checks the existence of the `#[has_significant_drop]` attribute
-struct SigDropChecker<'cx, 'sdt, 'tcx> {
+/// Checks the existence of the `#[has_significant_drop]` attribute.
+struct AttrChecker<'cx, 'others, 'tcx> {
cx: &'cx LateContext<'tcx>,
- seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
- type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
+ seen_types: &'others mut FxHashSet<Ty<'tcx>>,
+ type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
}
-impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
+impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
pub(crate) fn new(
cx: &'cx LateContext<'tcx>,
- seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
- type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
+ seen_types: &'others mut FxHashSet<Ty<'tcx>>,
+ type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
) -> Self {
seen_types.clear();
Self {
@@ -314,7 +162,17 @@ impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
}
}
- pub(crate) fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool {
+ fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
+ // The borrow checker prevents us from using something fancier like or_insert_with.
+ if let Some(ty) = self.type_cache.get(&ty) {
+ return *ty;
+ }
+ let value = self.has_sig_drop_attr_uncached(ty);
+ self.type_cache.insert(ty, value);
+ value
+ }
+
+ fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool {
if let Some(adt) = ty.ty_adt_def() {
let mut iter = get_attr(
self.cx.sess(),
@@ -333,7 +191,7 @@ impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
return true;
}
}
- for generic_arg in b.iter() {
+ for generic_arg in *b {
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
if self.has_sig_drop_attr(ty) {
return true;
@@ -350,73 +208,244 @@ impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
}
}
- pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
- // The borrow checker prevents us from using something fancier like or_insert_with.
- if let Some(ty) = self.type_cache.get(&ty) {
- return *ty;
- }
- let value = self.has_sig_drop_attr_uncached(ty);
- self.type_cache.insert(ty, value);
- value
- }
-
fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
!self.seen_types.insert(ty)
}
}
-/// Performs recursive calls to find any inner type marked with `#[has_significant_drop]`.
-struct SigDropFinder<'cx, 'sdt, 'tcx> {
- cx: &'cx LateContext<'tcx>,
- has_sig_drop: bool,
- sig_drop_checker: SigDropChecker<'cx, 'sdt, 'tcx>,
+struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
+ ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
+ cx: &'lc LateContext<'tcx>,
+ seen_types: &'others mut FxHashSet<Ty<'tcx>>,
+ type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
}
-impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> {
+impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
fn new(
- cx: &'cx LateContext<'tcx>,
- seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
- type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
+ ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
+ cx: &'lc LateContext<'tcx>,
+ seen_types: &'others mut FxHashSet<Ty<'tcx>>,
+ type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
) -> Self {
Self {
+ ap,
cx,
- has_sig_drop: false,
- sig_drop_checker: SigDropChecker::new(cx, seen_types, type_cache),
+ seen_types,
+ type_cache,
+ }
+ }
+
+ 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_expensive_expr(expr) => false,
+ 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() {
+ let last_stmt_is_not_dummy = apa.last_stmt_span != DUMMY_SP;
+ let last_stmt_is_not_curr = self.ap.curr_stmt.span != apa.last_stmt_span;
+ let block_equals_curr = self.ap.curr_block_hir_id == apa.first_block_hir_id;
+ let block_is_ancestor = self
+ .cx
+ .tcx
+ .hir()
+ .parent_iter(self.ap.curr_block_hir_id)
+ .any(|(id, _)| id == apa.first_block_hir_id);
+ if last_stmt_is_not_dummy && last_stmt_is_not_curr && (block_equals_curr || block_is_ancestor) {
+ apa.has_expensive_expr_after_last_attr = true;
+ }
+ }
}
}
}
-impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> {
- fn visit_expr(&mut self, ex: &'tcx hir::Expr<'_>) {
- if self
- .sig_drop_checker
- .has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
- {
- self.has_sig_drop = true;
- return;
+impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
+ fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
+ self.ap.curr_block_hir_id = block.hir_id;
+ self.ap.curr_block_span = block.span;
+ for stmt in block.stmts {
+ self.ap.curr_stmt = Cow::Borrowed(stmt);
+ self.visit_stmt(stmt);
+ self.ap.curr_block_hir_id = block.hir_id;
+ self.ap.curr_block_span = block.span;
+ self.manage_has_expensive_expr_after_last_attr();
+ }
+ if let Some(expr) = block.expr {
+ self.ap.curr_stmt = Cow::Owned(dummy_stmt_expr(expr));
+ self.visit_expr(expr);
+ self.ap.curr_block_hir_id = block.hir_id;
+ self.ap.curr_block_span = block.span;
+ self.manage_has_expensive_expr_after_last_attr();
}
+ }
- match ex.kind {
- hir::ExprKind::MethodCall(_, expr, ..) => {
- self.visit_expr(expr);
- },
- hir::ExprKind::Array(..)
- | hir::ExprKind::Assign(..)
- | hir::ExprKind::AssignOp(..)
- | hir::ExprKind::Binary(..)
- | hir::ExprKind::Call(..)
- | hir::ExprKind::Field(..)
- | hir::ExprKind::If(..)
- | hir::ExprKind::Index(..)
- | hir::ExprKind::Match(..)
- | hir::ExprKind::Repeat(..)
- | hir::ExprKind::Ret(..)
- | hir::ExprKind::Tup(..)
- | hir::ExprKind::Unary(..)
- | hir::ExprKind::Yield(..) => {
- walk_expr(self, ex);
- },
- _ => {},
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+ let modify_apa_params = |apa: &mut AuxParamsAttr| {
+ apa.counter = apa.counter.wrapping_add(1);
+ apa.has_expensive_expr_after_last_attr = false;
+ };
+ let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache);
+ if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
+ if let hir::StmtKind::Local(local) = self.ap.curr_stmt.kind
+ && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
+ && !self.ap.apas.contains_key(&hir_id)
+ && {
+ if let Some(local_hir_id) = path_to_local(expr) {
+ local_hir_id == hir_id
+ }
+ else {
+ true
+ }
+ }
+ {
+ let mut apa = AuxParamsAttr {
+ first_bind_ident: ident,
+ first_block_hir_id: self.ap.curr_block_hir_id,
+ first_block_span: self.ap.curr_block_span,
+ first_method_span: {
+ 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 {
+ expr_or_init.span
+ }
+ },
+ first_stmt_span: self.ap.curr_stmt.span,
+ ..Default::default()
+ };
+ 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; };
+ match self.ap.curr_stmt.kind {
+ hir::StmtKind::Local(local) => {
+ if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
+ apa.last_bind_ident = ident;
+ }
+ if let Some(local_init) = local.init
+ && let hir::ExprKind::MethodCall(_, _, _, span) = local_init.kind
+ {
+ apa.last_method_span = span;
+ }
+ },
+ hir::StmtKind::Semi(expr) => {
+ if has_drop(expr, &apa.first_bind_ident) {
+ apa.has_expensive_expr_after_last_attr = false;
+ apa.last_stmt_span = DUMMY_SP;
+ return;
+ }
+ if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind {
+ apa.last_method_span = span;
+ }
+ },
+ _ => {},
+ }
+ apa.last_stmt_span = self.ap.curr_stmt.span;
+ modify_apa_params(apa);
+ }
+ }
+ walk_expr(self, expr);
+ }
+}
+
+/// Auxiliary parameters used on each block check of an item
+struct AuxParams<'others, 'stmt, 'tcx> {
+ //// See [AuxParamsAttr].
+ apas: &'others mut FxIndexMap<hir::HirId, AuxParamsAttr>,
+ /// The current block identifier that is being visited.
+ curr_block_hir_id: hir::HirId,
+ /// The current block span that is being visited.
+ curr_block_span: Span,
+ /// The current statement that is being visited.
+ curr_stmt: Cow<'stmt, hir::Stmt<'tcx>>,
+}
+
+impl<'others, 'stmt, 'tcx> AuxParams<'others, 'stmt, 'tcx> {
+ fn new(apas: &'others mut FxIndexMap<hir::HirId, AuxParamsAttr>, curr_stmt: &'stmt hir::Stmt<'tcx>) -> Self {
+ Self {
+ apas,
+ curr_block_hir_id: hir::HirId::INVALID,
+ curr_block_span: DUMMY_SP,
+ curr_stmt: Cow::Borrowed(curr_stmt),
+ }
+ }
+}
+
+/// Auxiliary parameters used on expression created with `#[has_significant_drop]`.
+#[derive(Debug)]
+struct AuxParamsAttr {
+ /// The number of times `#[has_significant_drop]` was referenced.
+ counter: usize,
+ /// If an expensive expression follows the last use of anything marked with
+ /// `#[has_significant_drop]`.
+ has_expensive_expr_after_last_attr: bool,
+
+ /// The identifier of the block that involves the first `#[has_significant_drop]`.
+ first_block_hir_id: hir::HirId,
+ /// The span of the block that involves the first `#[has_significant_drop]`.
+ first_block_span: Span,
+ /// The binding or variable that references the initial construction of the type marked with
+ /// `#[has_significant_drop]`.
+ first_bind_ident: Ident,
+ /// Similar to `init_bind_ident` but encompasses the right-hand method call.
+ first_method_span: Span,
+ /// Similar to `init_bind_ident` but encompasses the whole contained statement.
+ first_stmt_span: Span,
+
+ /// The last visited binding or variable span within a block that had any referenced inner type
+ /// marked with `#[has_significant_drop]`.
+ last_bind_ident: Ident,
+ /// Similar to `last_bind_span` but encompasses the right-hand method call.
+ last_method_span: Span,
+ /// Similar to `last_bind_span` but encompasses the whole contained statement.
+ last_stmt_span: Span,
+}
+
+impl Default for AuxParamsAttr {
+ fn default() -> Self {
+ Self {
+ counter: 0,
+ has_expensive_expr_after_last_attr: false,
+ first_block_hir_id: hir::HirId::INVALID,
+ first_bind_ident: Ident::empty(),
+ first_block_span: DUMMY_SP,
+ first_method_span: DUMMY_SP,
+ first_stmt_span: DUMMY_SP,
+ last_bind_ident: Ident::empty(),
+ last_method_span: DUMMY_SP,
+ last_stmt_span: DUMMY_SP,
}
}
}
+
+fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> {
+ hir::Stmt {
+ hir_id: hir::HirId::INVALID,
+ kind: hir::StmtKind::Expr(expr),
+ span: DUMMY_SP,
+ }
+}
+
+fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident) -> bool {
+ if let hir::ExprKind::Call(fun, args) = expr.kind
+ && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
+ && let [fun_ident, ..] = fun_path.segments
+ && fun_ident.ident.name == rustc_span::sym::drop
+ && let [first_arg, ..] = args
+ && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind
+ && let [first_arg_ps, .. ] = arg_path.segments
+ {
+ &first_arg_ps.ident == first_bind_ident
+ }
+ else {
+ false
+ }
+}
+
+fn is_expensive_expr(expr: &hir::Expr<'_>) -> bool {
+ !matches!(expr.kind, hir::ExprKind::Path(_))
+}
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
new file mode 100644
index 000000000..42753d2e9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -0,0 +1,133 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{is_from_proc_macro, is_in_test_function};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::nested_filter::OnlyBodies;
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for functions that are only used once. Does not lint tests.
+ ///
+ /// ### Why is this bad?
+ /// It's usually not, splitting a function into multiple parts often improves readability and in
+ /// the case of generics, can prevent the compiler from duplicating the function dozens of
+ /// time; instead, only duplicating a thunk. But this can prevent segmentation across a
+ /// codebase, where many small functions are used only once.
+ ///
+ /// Note: If this lint is used, prepare to allow this a lot.
+ ///
+ /// ### Example
+ /// ```rust
+ /// pub fn a<T>(t: &T)
+ /// where
+ /// T: AsRef<str>,
+ /// {
+ /// a_inner(t.as_ref())
+ /// }
+ ///
+ /// fn a_inner(t: &str) {
+ /// /* snip */
+ /// }
+ ///
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// pub fn a<T>(t: &T)
+ /// where
+ /// T: AsRef<str>,
+ /// {
+ /// let t = t.as_ref();
+ /// /* snip */
+ /// }
+ ///
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub SINGLE_CALL_FN,
+ restriction,
+ "checks for functions that are only used once"
+}
+impl_lint_pass!(SingleCallFn => [SINGLE_CALL_FN]);
+
+#[derive(Clone)]
+pub struct SingleCallFn {
+ pub avoid_breaking_exported_api: bool,
+ pub def_id_to_usage: FxHashMap<LocalDefId, (Span, Vec<Span>)>,
+}
+
+impl<'tcx> LateLintPass<'tcx> for SingleCallFn {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ _: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ def_id: LocalDefId,
+ ) {
+ 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)
+ {
+ return;
+ }
+
+ self.def_id_to_usage.insert(def_id, (span, vec![]));
+ }
+
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ let mut v = FnUsageVisitor {
+ cx,
+ def_id_to_usage: &mut self.def_id_to_usage,
+ };
+ cx.tcx.hir().visit_all_item_likes_in_crate(&mut v);
+
+ for usage in self.def_id_to_usage.values() {
+ let single_call_fn_span = usage.0;
+ if let [caller_span] = *usage.1 {
+ span_lint_and_help(
+ cx,
+ SINGLE_CALL_FN,
+ single_call_fn_span,
+ "this function is only used once",
+ Some(caller_span),
+ "used here",
+ );
+ }
+ }
+ }
+}
+
+struct FnUsageVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ def_id_to_usage: &'a mut FxHashMap<LocalDefId, (Span, Vec<Span>)>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FnUsageVisitor<'a, 'tcx> {
+ type NestedFilter = OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ let Self { cx, .. } = *self;
+
+ if let ExprKind::Path(qpath) = expr.kind
+ && let res = cx.qpath_res(&qpath, expr.hir_id)
+ && let Some(call_def_id) = res.opt_def_id()
+ && let Some(def_id) = call_def_id.as_local()
+ && let Some(usage) = self.def_id_to_usage.get_mut(&def_id)
+ {
+ usage.1.push(expr.span);
+ }
+
+ walk_expr(self, expr);
+ }
+}
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
new file mode 100644
index 000000000..dfe8be7a6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -0,0 +1,147 @@
+use clippy_utils::{
+ diagnostics::span_lint_and_then, get_trait_def_id, higher::VecArgs, macros::root_macro_call_first_node,
+ source::snippet_opt, ty::implements_trait,
+};
+use rustc_ast::{LitIntType, LitKind, UintTy};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, LangItem, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use std::fmt::{self, Display, Formatter};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `Vec` or array initializations that contain only one range.
+ ///
+ /// ### Why is this bad?
+ /// This is almost always incorrect, as it will result in a `Vec` that has only one element.
+ /// Almost always, the programmer intended for it to include all elements in the range or for
+ /// the end of the range to be the length instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = [0..200];
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // 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
+ /// let x = [0; 200];
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub SINGLE_RANGE_IN_VEC_INIT,
+ suspicious,
+ "checks for initialization of `Vec` or arrays which consist of a single range"
+}
+declare_lint_pass!(SingleRangeInVecInit => [SINGLE_RANGE_IN_VEC_INIT]);
+
+enum SuggestedType {
+ Vec,
+ Array,
+}
+
+impl SuggestedType {
+ fn starts_with(&self) -> &'static str {
+ if matches!(self, SuggestedType::Vec) {
+ "vec!"
+ } else {
+ "["
+ }
+ }
+
+ fn ends_with(&self) -> &'static str {
+ if matches!(self, SuggestedType::Vec) { "" } else { "]" }
+ }
+}
+
+impl Display for SuggestedType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if matches!(&self, SuggestedType::Vec) {
+ write!(f, "a `Vec`")
+ } else {
+ write!(f, "an array")
+ }
+ }
+}
+
+impl LateLintPass<'_> for SingleRangeInVecInit {
+ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ // inner_expr: `vec![0..200]` or `[0..200]`
+ // ^^^^^^ ^^^^^^^
+ // span: `vec![0..200]` or `[0..200]`
+ // ^^^^^^^^^^^^ ^^^^^^^^
+ // suggested_type: What to print, "an array" or "a `Vec`"
+ let (inner_expr, span, suggested_type) = if let ExprKind::Array([inner_expr]) = expr.kind
+ && !expr.span.from_expansion()
+ {
+ (inner_expr, expr.span, SuggestedType::Array)
+ } else if let Some(macro_call) = root_macro_call_first_node(cx, expr)
+ && let Some(VecArgs::Vec([expr])) = VecArgs::hir(cx, expr)
+ {
+ (expr, macro_call.span, SuggestedType::Vec)
+ } else {
+ return;
+ };
+
+ let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], None) = inner_expr.kind else {
+ return;
+ };
+
+ if matches!(lang_item, LangItem::Range)
+ && let ty = cx.typeck_results().expr_ty(start.expr)
+ && let Some(snippet) = snippet_opt(cx, span)
+ // `is_from_proc_macro` will skip any `vec![]`. Let's not!
+ && snippet.starts_with(suggested_type.starts_with())
+ && snippet.ends_with(suggested_type.ends_with())
+ && let Some(start_snippet) = snippet_opt(cx, start.span)
+ && let Some(end_snippet) = snippet_opt(cx, end.span)
+ {
+ let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx, &["core", "iter", "Step"])
+ && implements_trait(cx, ty, step_def_id, &[])
+ {
+ true
+ } else {
+ false
+ };
+ let should_emit_of_len = if let Some(copy_def_id) = cx.tcx.lang_items().copy_trait()
+ && implements_trait(cx, ty, copy_def_id, &[])
+ && let ExprKind::Lit(lit_kind) = end.expr.kind
+ && let LitKind::Int(.., suffix_type) = lit_kind.node
+ && let LitIntType::Unsigned(UintTy::Usize) | LitIntType::Unsuffixed = suffix_type
+ {
+ true
+ } else {
+ false
+ };
+
+ if should_emit_every_value || should_emit_of_len {
+ span_lint_and_then(
+ cx,
+ SINGLE_RANGE_IN_VEC_INIT,
+ span,
+ &format!("{suggested_type} of `Range` that is only one element"),
+ |diag| {
+ if should_emit_every_value {
+ diag.span_suggestion(
+ span,
+ "if you wanted a `Vec` that contains the entire range, try",
+ format!("({start_snippet}..{end_snippet}).collect::<std::vec::Vec<{ty}>>()"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ if should_emit_of_len {
+ diag.span_suggestion(
+ inner_expr.span,
+ format!("if you wanted {suggested_type} of len {end_snippet}, try"),
+ format!("{start_snippet}; {end_snippet}"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ },
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 483f860a8..8658009eb 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq};
-use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
+use clippy_utils::{get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
@@ -255,7 +255,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
if_chain! {
// Find std::str::converts::from_utf8
- if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8);
+ if let ExprKind::Call(fun, args) = e.kind;
+ if is_path_diagnostic_item(cx, fun, sym::str_from_utf8);
// Find string::as_bytes
if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 4ccda1506..6db330dfa 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,6 +1,7 @@
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::{SpanlessEq, SpanlessHash};
+use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher};
use if_chain::if_chain;
use itertools::Itertools;
@@ -9,7 +10,7 @@ use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{
- GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
+ GenericArg, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
@@ -86,15 +87,16 @@ declare_clippy_lint! {
"check if the same trait bounds are specified more than once during a generic declaration"
}
-#[derive(Copy, Clone)]
+#[derive(Clone)]
pub struct TraitBounds {
max_trait_bounds: u64,
+ msrv: Msrv,
}
impl TraitBounds {
#[must_use]
- pub fn new(max_trait_bounds: u64) -> Self {
- Self { max_trait_bounds }
+ pub fn new(max_trait_bounds: u64, msrv: Msrv) -> Self {
+ Self { max_trait_bounds, msrv }
}
}
@@ -139,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
) = cx.tcx.hir().get_if_local(*def_id);
then {
if self_bounds_map.is_empty() {
- for bound in self_bounds.iter() {
+ for bound in *self_bounds {
let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
self_bounds_map.insert(self_res, self_segments);
}
@@ -184,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
// Iterate the bounds and add them to our seen hash
// If we haven't yet seen it, add it to the fixed traits
- for bound in bounds.iter() {
+ for bound in bounds {
let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; };
let new_trait = seen_def_ids.insert(def_id);
@@ -222,10 +224,24 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
}
}
}
+
+ extract_msrv_attr!(LateContext);
}
impl TraitBounds {
- fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
+ /// Is the given bound a `?Sized` bound, and is combining it (i.e. `T: X + ?Sized`) an error on
+ /// this MSRV? See <https://github.com/rust-lang/rust-clippy/issues/8772> for details.
+ fn cannot_combine_maybe_bound(&self, cx: &LateContext<'_>, bound: &GenericBound<'_>) -> bool {
+ if !self.msrv.meets(msrvs::MAYBE_BOUND_IN_WHERE)
+ && let GenericBound::Trait(tr, TraitBoundModifier::Maybe) = bound
+ {
+ cx.tcx.lang_items().get(LangItem::Sized) == tr.trait_ref.path.res.opt_def_id()
+ } else {
+ false
+ }
+ }
+
+ fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
struct SpanlessTy<'cx, 'tcx> {
ty: &'tcx Ty<'tcx>,
cx: &'cx LateContext<'tcx>,
@@ -256,11 +272,10 @@ impl TraitBounds {
if p.origin != PredicateOrigin::ImplTrait;
if p.bounds.len() as u64 <= self.max_trait_bounds;
if !p.span.from_expansion();
- if let Some(ref v) = map.insert(
- SpanlessTy { ty: p.bounded_ty, cx },
- p.bounds.iter().collect::<Vec<_>>()
- );
-
+ let bounds = p.bounds.iter().filter(|b| !self.cannot_combine_maybe_bound(cx, b)).collect::<Vec<_>>();
+ if !bounds.is_empty();
+ if let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds);
+ if !is_from_proc_macro(cx, p.bounded_ty);
then {
let trait_bounds = v
.iter()
@@ -342,7 +357,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
"this trait bound is already specified in the where clause",
None,
"consider removing this trait bound",
- );
+ );
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
index 31a9b69ca..857d2ad82 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
@@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
"transmute from a pointer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
- let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
+ let sugg = arg.as_ty(Ty::new_ptr(cx.tcx,*to_ty));
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
}
},
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
index 426c72538..ea9ad9961 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
@@ -64,8 +64,8 @@ pub(super) fn check<'tcx>(
};
let ty_to_and_mut = ty::TypeAndMut { ty: *ty_to, mutbl: *to_mutbl };
let sugg_paren = arg
- .as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
- .as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
+ .as_ty(Ty::new_ptr(cx.tcx,ty_from_and_mut))
+ .as_ty(Ty::new_ptr(cx.tcx,ty_to_and_mut));
let sugg = if *to_mutbl == Mutability::Mut {
sugg_paren.mut_addr_deref()
} else {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
index 56207fe76..b6615410e 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
let sugg = if *ptr_ty == rty_and_mut {
arg.as_ty(to_ty)
} else {
- arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
+ arg.as_ty(Ty::new_ptr(cx.tcx,rty_and_mut)).as_ty(to_ty)
};
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
new file mode 100644
index 000000000..90eb45a09
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -0,0 +1,235 @@
+use clippy_utils::{
+ diagnostics::span_lint_and_help,
+ is_from_proc_macro,
+ msrvs::{self, Msrv},
+ path_to_local,
+};
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind, HirId, Node, Pat};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::{lint::in_external_macro, ty};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use std::iter::once;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for tuple<=>array conversions that are not done with `.into()`.
+ ///
+ /// ### Why is this bad?
+ /// It's unnecessary complexity. `.into()` works for tuples<=>arrays at or below 12 elements and
+ /// conveys the intent a lot better, while also leaving less room for hard to spot bugs!
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let t1 = &[(1, 2), (3, 4)];
+ /// let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let t1 = &[(1, 2), (3, 4)];
+ /// let v1: Vec<[u32; 2]> = t1.iter().map(|&t| t.into()).collect();
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub TUPLE_ARRAY_CONVERSIONS,
+ nursery,
+ "checks for tuple<=>array conversions that are not done with `.into()`"
+}
+impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]);
+
+#[derive(Clone)]
+pub struct TupleArrayConversions {
+ pub msrv: Msrv,
+}
+
+impl LateLintPass<'_> for TupleArrayConversions {
+ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
+ match expr.kind {
+ ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
+ ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
+ _ => {},
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
+
+#[expect(
+ clippy::blocks_in_if_conditions,
+ reason = "not a FP, but this is much easier to understand"
+)]
+fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
+ if should_lint(
+ cx,
+ elements,
+ // This is cursed.
+ Some,
+ |(first_id, local)| {
+ if let Node::Pat(pat) = local
+ && let parent = parent_pat(cx, pat)
+ && parent.hir_id == first_id
+ {
+ return matches!(
+ cx.typeck_results().pat_ty(parent).peel_refs().kind(),
+ ty::Tuple(len) if len.len() == elements.len()
+ );
+ }
+
+ false
+ },
+ ) || should_lint(
+ cx,
+ elements,
+ |(i, expr)| {
+ if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() {
+ return Some((i, path));
+ };
+
+ None
+ },
+ |(first_id, local)| {
+ if let Node::Pat(pat) = local
+ && let parent = parent_pat(cx, pat)
+ && parent.hir_id == first_id
+ {
+ return matches!(
+ cx.typeck_results().pat_ty(parent).peel_refs().kind(),
+ ty::Tuple(len) if len.len() == elements.len()
+ );
+ }
+
+ false
+ },
+ ) {
+ emit_lint(cx, expr, ToType::Array);
+ }
+}
+
+#[expect(
+ clippy::blocks_in_if_conditions,
+ reason = "not a FP, but this is much easier to understand"
+)]
+#[expect(clippy::cast_possible_truncation)]
+fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
+ if should_lint(cx, elements, Some, |(first_id, local)| {
+ if let Node::Pat(pat) = local
+ && let parent = parent_pat(cx, pat)
+ && parent.hir_id == first_id
+ {
+ return matches!(
+ cx.typeck_results().pat_ty(parent).peel_refs().kind(),
+ ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
+ );
+ }
+
+ false
+ }) || should_lint(
+ cx,
+ elements,
+ |(i, expr)| {
+ if let ExprKind::Index(path, index) = expr.kind
+ && let ExprKind::Lit(lit) = index.kind
+ && let LitKind::Int(val, _) = lit.node
+ && val as usize == i
+ {
+ return Some((i, path));
+ };
+
+ None
+ },
+ |(first_id, local)| {
+ if let Node::Pat(pat) = local
+ && let parent = parent_pat(cx, pat)
+ && parent.hir_id == first_id
+ {
+ return matches!(
+ cx.typeck_results().pat_ty(parent).peel_refs().kind(),
+ ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
+ );
+ }
+
+ false
+ },
+ ) {
+ emit_lint(cx, expr, ToType::Tuple);
+ }
+}
+
+/// Walks up the `Pat` until it's reached the final containing `Pat`.
+fn parent_pat<'tcx>(cx: &LateContext<'tcx>, start: &'tcx Pat<'tcx>) -> &'tcx Pat<'tcx> {
+ let mut end = start;
+ for (_, node) in cx.tcx.hir().parent_iter(start.hir_id) {
+ if let Node::Pat(pat) = node {
+ end = pat;
+ } else {
+ break;
+ }
+ }
+ end
+}
+
+#[derive(Clone, Copy)]
+enum ToType {
+ Array,
+ Tuple,
+}
+
+impl ToType {
+ fn msg(self) -> &'static str {
+ match self {
+ ToType::Array => "it looks like you're trying to convert a tuple to an array",
+ ToType::Tuple => "it looks like you're trying to convert an array to a tuple",
+ }
+ }
+
+ fn help(self) -> &'static str {
+ match self {
+ ToType::Array => "use `.into()` instead, or `<[T; N]>::from` if type annotations are needed",
+ ToType::Tuple => "use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed",
+ }
+ }
+}
+
+fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToType) -> bool {
+ if !is_from_proc_macro(cx, expr) {
+ span_lint_and_help(
+ cx,
+ TUPLE_ARRAY_CONVERSIONS,
+ expr.span,
+ to_type.msg(),
+ None,
+ to_type.help(),
+ );
+
+ return true;
+ }
+
+ false
+}
+
+fn should_lint<'tcx>(
+ cx: &LateContext<'tcx>,
+ elements: &'tcx [Expr<'tcx>],
+ map: impl FnMut((usize, &'tcx Expr<'tcx>)) -> Option<(usize, &Expr<'_>)>,
+ predicate: impl FnMut((HirId, &Node<'tcx>)) -> bool,
+) -> bool {
+ if let Some(elements) = elements
+ .iter()
+ .enumerate()
+ .map(map)
+ .collect::<Option<Vec<_>>>()
+ && let Some(locals) = elements
+ .iter()
+ .map(|(_, element)| path_to_local(element).and_then(|local| cx.tcx.hir().find(local)))
+ .collect::<Option<Vec<_>>>()
+ && let [first, rest @ ..] = &*locals
+ && let Node::Pat(first_pat) = first
+ && let parent = parent_pat(cx, first_pat).hir_id
+ && rest.iter().chain(once(first)).map(|i| (parent, i)).all(predicate)
+ {
+ return true;
+ }
+
+ false
+}
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 2920684ad..a9deee967 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -11,7 +11,7 @@ use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Pos, Span, SyntaxContext};
declare_clippy_lint! {
@@ -92,7 +92,22 @@ declare_clippy_lint! {
"annotating safe code with a safety comment"
}
-declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
+#[derive(Copy, Clone)]
+pub struct UndocumentedUnsafeBlocks {
+ accept_comment_above_statement: bool,
+ accept_comment_above_attributes: bool,
+}
+
+impl UndocumentedUnsafeBlocks {
+ pub fn new(accept_comment_above_statement: bool, accept_comment_above_attributes: bool) -> Self {
+ Self {
+ accept_comment_above_statement,
+ accept_comment_above_attributes,
+ }
+ }
+}
+
+impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
@@ -101,7 +116,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
&& !is_unsafe_from_proc_macro(cx, block.span)
&& !block_has_safety_comment(cx, block.span)
- && !block_parents_have_safety_comment(cx, block.hir_id)
+ && !block_parents_have_safety_comment(
+ self.accept_comment_above_statement,
+ self.accept_comment_above_attributes,
+ cx,
+ block.hir_id,
+ )
{
let source_map = cx.tcx.sess.source_map();
let span = if source_map.is_multiline(block.span) {
@@ -313,29 +333,95 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
// Checks if any parent {expression, statement, block, local, const, static}
// has a safety comment
-fn block_parents_have_safety_comment(cx: &LateContext<'_>, id: hir::HirId) -> bool {
+fn block_parents_have_safety_comment(
+ accept_comment_above_statement: bool,
+ accept_comment_above_attributes: bool,
+ cx: &LateContext<'_>,
+ id: hir::HirId,
+) -> bool {
if let Some(node) = get_parent_node(cx.tcx, id) {
return match node {
- Node::Expr(expr) => !is_branchy(expr) && span_in_body_has_safety_comment(cx, expr.span),
+ Node::Expr(expr) => {
+ if let Some(
+ Node::Local(hir::Local { span, .. })
+ | Node::Item(hir::Item {
+ kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
+ span,
+ ..
+ }),
+ ) = get_parent_node(cx.tcx, expr.hir_id)
+ {
+ let hir_id = match get_parent_node(cx.tcx, expr.hir_id) {
+ Some(Node::Local(hir::Local { hir_id, .. })) => *hir_id,
+ Some(Node::Item(hir::Item { owner_id, .. })) => {
+ cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)
+ },
+ _ => unreachable!(),
+ };
+
+ // if unsafe block is part of a let/const/static statement,
+ // and accept_comment_above_statement is set to true
+ // we accept the safety comment in the line the precedes this statement.
+ accept_comment_above_statement
+ && span_with_attrs_in_body_has_safety_comment(
+ cx,
+ *span,
+ hir_id,
+ accept_comment_above_attributes,
+ )
+ } else {
+ !is_branchy(expr)
+ && span_with_attrs_in_body_has_safety_comment(
+ cx,
+ expr.span,
+ expr.hir_id,
+ accept_comment_above_attributes,
+ )
+ }
+ },
Node::Stmt(hir::Stmt {
kind:
- hir::StmtKind::Local(hir::Local { span, .. })
- | hir::StmtKind::Expr(hir::Expr { span, .. })
- | hir::StmtKind::Semi(hir::Expr { span, .. }),
+ hir::StmtKind::Local(hir::Local { span, hir_id, .. })
+ | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. })
+ | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
..
})
- | Node::Local(hir::Local { span, .. })
- | Node::Item(hir::Item {
+ | Node::Local(hir::Local { span, hir_id, .. }) => {
+ span_with_attrs_in_body_has_safety_comment(cx, *span, *hir_id, accept_comment_above_attributes)
+ },
+ Node::Item(hir::Item {
kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
span,
+ owner_id,
..
- }) => span_in_body_has_safety_comment(cx, *span),
+ }) => span_with_attrs_in_body_has_safety_comment(
+ cx,
+ *span,
+ cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id),
+ accept_comment_above_attributes,
+ ),
_ => false,
};
}
false
}
+/// Extends `span` to also include its attributes, then checks if that span has a safety comment.
+fn span_with_attrs_in_body_has_safety_comment(
+ cx: &LateContext<'_>,
+ span: Span,
+ hir_id: HirId,
+ accept_comment_above_attributes: bool,
+) -> bool {
+ let span = if accept_comment_above_attributes {
+ include_attrs_in_span(cx, hir_id, span)
+ } else {
+ span
+ };
+
+ span_in_body_has_safety_comment(cx, span)
+}
+
/// Checks if an expression is "branchy", e.g. loop, match/if/etc.
fn is_branchy(expr: &hir::Expr<'_>) -> bool {
matches!(
@@ -360,6 +446,15 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
) || span_in_body_has_safety_comment(cx, span)
}
+fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span {
+ span.to(cx
+ .tcx
+ .hir()
+ .attrs(hir_id)
+ .iter()
+ .fold(span, |acc, attr| acc.to(attr.span)))
+}
+
enum HasSafetyComment {
Yes(BytePos),
No,
@@ -546,7 +641,14 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
for (_, node) in map.parent_iter(body.hir_id) {
match node {
Node::Expr(e) => span = e.span,
- Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
+ Node::Block(_)
+ | Node::Arm(_)
+ | Node::Stmt(_)
+ | Node::Local(_)
+ | Node::Item(hir::Item {
+ kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
+ ..
+ }) => (),
_ => break,
}
}
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 289ca4e9b..99a1d1976 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
@@ -4,7 +4,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{Closure, Expr, ExprKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
-use rustc_middle::ty::{Clause, GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate};
+use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, BytePos, Span};
@@ -45,7 +45,7 @@ fn get_trait_predicates_for_trait_id<'tcx>(
let mut preds = Vec::new();
for (pred, _) in generics.predicates {
if_chain! {
- if let PredicateKind::Clause(Clause::Trait(poly_trait_pred)) = pred.kind().skip_binder();
+ if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder();
let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred));
if let Some(trait_def_id) = trait_id;
if trait_def_id == trait_pred.trait_ref.def_id;
@@ -63,7 +63,7 @@ fn get_projection_pred<'tcx>(
trait_pred: TraitPredicate<'tcx>,
) -> Option<ProjectionPredicate<'tcx>> {
generics.predicates.iter().find_map(|(proj_pred, _)| {
- if let ty::PredicateKind::Clause(Clause::Projection(pred)) = proj_pred.kind().skip_binder() {
+ if let ClauseKind::Projection(pred) = proj_pred.kind().skip_binder() {
let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred));
if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs {
return Some(projection_pred);
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index 0bcafde65..0f5cdb6aa 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -96,9 +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) ||
- match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
- match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
+ if match_def_path(cx, def_id, &paths::PTR_EQ);
let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0);
if ty_param.is_trait();
then {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 8b0e0ce5a..5073eb02b 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -163,7 +163,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
diag.span_suggestion(
fn_decl.output.span(),
- return_type_sugg_msg.as_str(),
+ return_type_sugg_msg,
return_type_sugg,
Applicability::MaybeIncorrect,
);
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 55651a28b..5e42cf7e4 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,5 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_def_id_trait_method;
+use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
@@ -42,6 +43,10 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
struct AsyncFnVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
found_await: bool,
+ /// Also keep track of `await`s in nested async blocks so we can mention
+ /// it in a note
+ await_in_async_block: Option<Span>,
+ async_depth: usize,
}
impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
@@ -49,7 +54,11 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind {
- self.found_await = true;
+ if self.async_depth == 1 {
+ self.found_await = true;
+ } else if self.await_in_async_block.is_none() {
+ self.await_in_async_block = Some(ex.span);
+ }
}
walk_expr(self, ex);
}
@@ -57,6 +66,20 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
+
+ fn visit_body(&mut self, b: &'tcx Body<'tcx>) {
+ let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_)));
+
+ if is_async_block {
+ self.async_depth += 1;
+ }
+
+ walk_body(self, b);
+
+ if is_async_block {
+ self.async_depth -= 1;
+ }
+ }
}
impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
@@ -69,17 +92,31 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
span: Span,
def_id: LocalDefId,
) {
- if !span.from_expansion() && fn_kind.asyncness().is_async() {
- let mut visitor = AsyncFnVisitor { cx, found_await: false };
+ if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) {
+ let mut visitor = AsyncFnVisitor {
+ cx,
+ found_await: false,
+ async_depth: 0,
+ await_in_async_block: None,
+ };
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
if !visitor.found_await {
- span_lint_and_help(
+ span_lint_and_then(
cx,
UNUSED_ASYNC,
span,
"unused `async` for function with no await statements",
- None,
- "consider removing the `async` from this function",
+ |diag| {
+ diag.help("consider removing the `async` from this function");
+
+ if let Some(span) = visitor.await_in_async_block {
+ diag.span_note(
+ span,
+ "`await` used in an async block, which does not require \
+ the enclosing function to be `async`",
+ );
+ }
+ },
);
}
}
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 1d2d3eb12..4df1e3299 100644
--- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
+++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
@@ -65,7 +65,7 @@ fn correct_ident(ident: &str) -> String {
let mut ident = fragments.clone().next().unwrap();
for (ref prev, ref curr) in fragments.tuple_windows() {
- if [prev, curr]
+ if <[&String; 2]>::from((prev, curr))
.iter()
.all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase())
{
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 28c3fc859..22de383ea 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -1,16 +1,18 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_ty_alias;
-use clippy_utils::source::{snippet, snippet_with_context};
+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, match_def_path, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -39,14 +41,74 @@ declare_clippy_lint! {
#[derive(Default)]
pub struct UselessConversion {
try_desugar_arm: Vec<HirId>,
+ expn_depth: u32,
}
impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
+enum MethodOrFunction {
+ Method,
+ Function,
+}
+
+impl MethodOrFunction {
+ /// Maps the argument position in `pos` to the parameter position.
+ /// For methods, `self` is skipped.
+ fn param_pos(self, pos: usize) -> usize {
+ match self {
+ MethodOrFunction::Method => pos + 1,
+ MethodOrFunction::Function => pos,
+ }
+ }
+}
+
+/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`
+fn into_iter_bound(cx: &LateContext<'_>, fn_did: DefId, into_iter_did: DefId, param_index: u32) -> Option<Span> {
+ cx.tcx
+ .predicates_of(fn_did)
+ .predicates
+ .iter()
+ .find_map(|&(ref pred, span)| {
+ if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder()
+ && tr.def_id() == into_iter_did
+ && tr.self_ty().is_param(param_index)
+ {
+ Some(span)
+ } else {
+ None
+ }
+ })
+}
+
+/// Extracts the receiver of a `.into_iter()` method call.
+fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> {
+ if let ExprKind::MethodCall(name, recv, _, _) = expr.kind
+ && is_trait_method(cx, expr, sym::IntoIterator)
+ && name.ident.name == sym::into_iter
+ {
+ Some(recv)
+ } else {
+ None
+ }
+}
+
+/// Same as [`into_iter_call`], but tries to look for the innermost `.into_iter()` call, e.g.:
+/// `foo.into_iter().into_iter()`
+/// ^^^ we want this expression
+fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> (&'hir Expr<'hir>, usize) {
+ let mut depth = 0;
+ while let Some(recv) = into_iter_call(cx, expr) {
+ expr = recv;
+ depth += 1;
+ }
+ (expr, depth)
+}
+
#[expect(clippy::too_many_lines)]
impl<'tcx> LateLintPass<'tcx> for UselessConversion {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if e.span.from_expansion() {
+ self.expn_depth += 1;
return;
}
@@ -82,9 +144,65 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
);
}
}
- if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter {
- if get_parent_expr(cx, e).is_some() &&
- let Some(id) = path_to_local(recv) &&
+ if let Some(into_iter_recv) = into_iter_call(cx, e)
+ // Make sure that there is no parent expression, or if there is, make sure it's not a `.into_iter()` call.
+ // The reason for that is that we only want to lint once (the outermost call)
+ // in cases like `foo.into_iter().into_iter()`
+ && get_parent_expr(cx, e)
+ .and_then(|parent| into_iter_call(cx, parent))
+ .is_none()
+ {
+ if let Some(parent) = get_parent_expr(cx, e) {
+ let parent_fn = match parent.kind {
+ ExprKind::Call(recv, args)
+ if let ExprKind::Path(ref qpath) = recv.kind
+ && let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
+ // make sure that the path indeed points to a fn-like item, so that
+ // `fn_sig` does not ICE. (see #11065)
+ && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
+ {
+ Some((did, args, MethodOrFunction::Function))
+ }
+ ExprKind::MethodCall(.., args, _) => {
+ cx.typeck_results().type_dependent_def_id(parent.hir_id)
+ .map(|did| (did, args, MethodOrFunction::Method))
+ }
+ _ => None,
+ };
+
+ if let Some((parent_fn_did, args, kind)) = parent_fn
+ && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
+ && let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder()
+ && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id)
+ && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
+ && let ty::Param(param) = into_iter_param.kind()
+ && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
+ && self.expn_depth == 0
+ {
+ // Get the "innermost" `.into_iter()` call, e.g. given this expression:
+ // `foo.into_iter().into_iter()`
+ // ^^^
+ let (into_iter_recv, depth) = into_iter_deep_call(cx, into_iter_recv);
+
+ 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()`");
+ });
+
+ // 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
@@ -195,5 +313,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if Some(&e.hir_id) == self.try_desugar_arm.last() {
self.try_desugar_arm.pop();
}
+ if e.span.from_expansion() {
+ self.expn_depth -= 1;
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 3c2bf5aba..6b51974d7 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -559,6 +559,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Ret({value})");
value.if_some(|e| self.expr(e));
},
+ ExprKind::Become(value) => {
+ bind!(self, value);
+ kind!("Become({value})");
+ self.expr(value);
+ },
ExprKind::InlineAsm(_) => {
kind!("InlineAsm(_)");
out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index f6de66bb5..f1d05c752 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -2,12 +2,15 @@
#![allow(clippy::module_name_repetitions)]
+use rustc_session::Session;
+use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
use serde::Deserialize;
-use std::error::Error;
+use std::fmt::{Debug, Display, Formatter};
+use std::ops::Range;
use std::path::{Path, PathBuf};
use std::str::FromStr;
-use std::{cmp, env, fmt, fs, io, iter};
+use std::{cmp, env, fmt, fs, io};
#[rustfmt::skip]
const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
@@ -18,6 +21,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
"GitHub", "GitLab",
"IPv4", "IPv6",
"ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
+ "WebAssembly",
"NaN", "NaNs",
"OAuth", "GraphQL",
"OCaml",
@@ -31,6 +35,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
"CamelCase",
];
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)]
@@ -67,33 +72,70 @@ impl DisallowedPath {
#[derive(Default)]
pub struct TryConf {
pub conf: Conf,
- pub errors: Vec<Box<dyn Error>>,
- pub warnings: Vec<Box<dyn Error>>,
+ pub errors: Vec<ConfError>,
+ pub warnings: Vec<ConfError>,
}
impl TryConf {
- fn from_error(error: impl Error + 'static) -> Self {
+ 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![Box::new(error)],
+ errors: vec![value],
warnings: vec![],
}
}
}
+impl From<io::Error> for TryConf {
+ fn from(value: io::Error) -> Self {
+ ConfError::from(value).into()
+ }
+}
+
#[derive(Debug)]
-struct ConfError(String);
+pub struct ConfError {
+ pub message: String,
+ pub span: Option<Span>,
+}
-impl fmt::Display for ConfError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- <String as fmt::Display>::fmt(&self.0, f)
+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,
+ }
+ }
}
-}
-impl Error for ConfError {}
+ fn spanned(file: &SourceFile, message: impl Into<String>, span: Range<usize>) -> Self {
+ Self {
+ message: message.into(),
+ span: Some(Span::new(
+ file.start_pos + BytePos::from_usize(span.start),
+ file.start_pos + BytePos::from_usize(span.end),
+ SyntaxContext::root(),
+ None,
+ )),
+ }
+ }
+}
-fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
- Box::new(ConfError(s.into()))
+impl From<io::Error> for ConfError {
+ fn from(value: io::Error) -> Self {
+ Self {
+ message: value.to_string(),
+ span: None,
+ }
+ }
}
macro_rules! define_Conf {
@@ -117,20 +159,14 @@ macro_rules! define_Conf {
}
}
- impl<'de> Deserialize<'de> for TryConf {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
- deserializer.deserialize_map(ConfVisitor)
- }
- }
-
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "kebab-case")]
#[allow(non_camel_case_types)]
enum Field { $($name,)* third_party, }
- struct ConfVisitor;
+ struct ConfVisitor<'a>(&'a SourceFile);
- impl<'de> Visitor<'de> for ConfVisitor {
+ impl<'de> Visitor<'de> for ConfVisitor<'_> {
type Value = TryConf;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -141,32 +177,38 @@ macro_rules! define_Conf {
let mut errors = Vec::new();
let mut warnings = Vec::new();
$(let mut $name = None;)*
- // could get `Field` here directly, but get `str` first for diagnostics
- while let Some(name) = map.next_key::<&str>()? {
- match Field::deserialize(name.into_deserializer())? {
- $(Field::$name => {
- $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
- match map.next_value() {
- Err(e) => errors.push(conf_error(e.to_string())),
+ // could get `Field` here directly, but get `String` first for diagnostics
+ while let Some(name) = map.next_key::<toml::Spanned<String>>()? {
+ match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
+ Err(e) => {
+ let e: FieldError = e;
+ errors.push(ConfError::spanned(self.0, e.0, name.span()));
+ }
+ $(Ok(Field::$name) => {
+ $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)?
+ let raw_value = map.next_value::<toml::Spanned<toml::Value>>()?;
+ let value_span = raw_value.span();
+ match <$ty>::deserialize(raw_value.into_inner()) {
+ Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)),
Ok(value) => match $name {
- Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
+ Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())),
None => {
$name = Some(value);
// $new_conf is the same as one of the defined `$name`s, so
// this variable is defined in line 2 of this function.
$(match $new_conf {
- Some(_) => errors.push(conf_error(concat!(
+ Some(_) => errors.push(ConfError::spanned(self.0, concat!(
"duplicate field `", stringify!($new_conf),
"` (provided as `", stringify!($name), "`)"
- ))),
+ ), name.span())),
None => $new_conf = $name.clone(),
})?
},
}
}
})*
- // white-listed; ignore
- Field::third_party => drop(map.next_value::<IgnoredAny>())
+ // ignore contents of the third_party key
+ Ok(Field::third_party) => drop(map.next_value::<IgnoredAny>())
}
}
let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
@@ -248,11 +290,11 @@ define_Conf! {
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
/// ```
(arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::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.
+ /// 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, 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.
+ /// 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.
///
/// The minimum rust version that the project supports
(msrv: Option<String> = None),
@@ -265,6 +307,10 @@ define_Conf! {
///
/// The maximum cognitive complexity a function can have
(cognitive_complexity_threshold: u64 = 25),
+ /// Lint: EXCESSIVE_NESTING.
+ ///
+ /// The maximum amount of nesting a block can reside in
+ (excessive_nesting_threshold: u64 = 0),
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
///
/// Use the Cognitive Complexity lint instead.
@@ -342,6 +388,10 @@ define_Conf! {
///
/// The maximum allowed size for arrays on the stack
(array_size_threshold: u64 = 512_000),
+ /// Lint: LARGE_STACK_FRAMES.
+ ///
+ /// The maximum allowed stack size for functions in bytes
+ (stack_size_threshold: u64 = 512_000),
/// Lint: VEC_BOX.
///
/// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
@@ -474,6 +524,33 @@ define_Conf! {
///
/// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
(unnecessary_box_size: u64 = 128),
+ /// Lint: MODULE_INCEPTION.
+ ///
+ /// Whether to allow module inception if it's not public.
+ (allow_private_module_inception: bool = false),
+ /// Lint: MIN_IDENT_CHARS.
+ ///
+ /// 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()),
+ /// Lint: MIN_IDENT_CHARS.
+ ///
+ /// Minimum chars an ident can have, anything below or equal to this will be linted.
+ (min_ident_chars_threshold: u64 = 1),
+ /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
+ ///
+ /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
+ (accept_comment_above_statement: bool = false),
+ /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
+ ///
+ /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
+ (accept_comment_above_attributes: bool = false),
+ /// Lint: UNNECESSARY_RAW_STRING_HASHES.
+ ///
+ /// Whether to allow `r#""#` when `r""` can be used
+ (allow_one_hash_in_raw_strings: bool = false),
}
/// Search for the configuration file.
@@ -486,7 +563,7 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
// Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
- // If neither of those exist, use ".".
+ // If neither of those exist, use ".". (Update documentation if this priority changes)
let mut current = env::var_os("CLIPPY_CONF_DIR")
.or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
.map_or_else(|| PathBuf::from("."), PathBuf::from)
@@ -532,19 +609,25 @@ 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(path: &Path) -> TryConf {
- let content = match fs::read_to_string(path) {
- Err(e) => return TryConf::from_error(e),
- Ok(content) => content,
+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::from_str::<TryConf>(&content) {
+ 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);
+ // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
+ if conf.conf.allowed_idents_below_min_chars.contains(&"..".to_owned()) {
+ conf.conf
+ .allowed_idents_below_min_chars
+ .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
+ }
conf
},
- Err(e) => TryConf::from_error(e),
+ Err(e) => TryConf::from_toml_error(&file, &e),
}
}
@@ -556,65 +639,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
const SEPARATOR_WIDTH: usize = 4;
-// Check whether the error is "unknown field" and, if so, list the available fields sorted and at
-// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
-pub fn format_error(error: Box<dyn Error>) -> String {
- let s = error.to_string();
-
- if_chain! {
- if error.downcast::<toml::de::Error>().is_ok();
- if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
- then {
- use fmt::Write;
-
- fields.sort_unstable();
-
- let (rows, column_widths) = calculate_dimensions(&fields);
-
- let mut msg = String::from(prefix);
- for row in 0..rows {
- writeln!(msg).unwrap();
- for (column, column_width) in column_widths.iter().copied().enumerate() {
- let index = column * rows + row;
- let field = fields.get(index).copied().unwrap_or_default();
- write!(
- msg,
- "{:SEPARATOR_WIDTH$}{field:column_width$}",
- " "
- )
- .unwrap();
- }
- }
- write!(msg, "\n{suffix}").unwrap();
- msg
- } else {
- s
- }
+#[derive(Debug)]
+struct FieldError(String);
+
+impl std::error::Error for FieldError {}
+
+impl Display for FieldError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.pad(&self.0)
}
}
-// `parse_unknown_field_message` will become unnecessary if
-// https://github.com/alexcrichton/toml-rs/pull/364 is merged.
-fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> {
- // An "unknown field" message has the following form:
- // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
- // ^^ ^^^^ ^^
- if_chain! {
- if s.starts_with("unknown field");
- let slices = s.split("`, `").collect::<Vec<_>>();
- let n = slices.len();
- if n >= 2;
- if let Some((prefix, first_field)) = slices[0].rsplit_once(" `");
- if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
- then {
- let fields = iter::once(first_field)
- .chain(slices[1..n - 1].iter().copied())
- .chain(iter::once(last_field))
- .collect::<Vec<_>>();
- Some((prefix, fields, suffix))
- } else {
- None
+impl serde::de::Error for FieldError {
+ fn custom<T: Display>(msg: T) -> Self {
+ Self(msg.to_string())
+ }
+
+ fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
+ // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is
+ // set and allows it.
+ use fmt::Write;
+
+ let mut expected = expected.to_vec();
+ expected.sort_unstable();
+
+ let (rows, column_widths) = calculate_dimensions(&expected);
+
+ let mut msg = format!("unknown field `{field}`, expected one of");
+ for row in 0..rows {
+ writeln!(msg).unwrap();
+ for (column, column_width) in column_widths.iter().copied().enumerate() {
+ let index = column * rows + row;
+ let field = expected.get(index).copied().unwrap_or_default();
+ write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap();
+ }
}
+ Self(msg)
}
}
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 71f6c9909..e222a5448 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,3 +1,4 @@
+pub mod almost_standard_lint_formulation;
pub mod clippy_lints_internal;
pub mod collapsible_calls;
pub mod compiler_lint_functions;
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
new file mode 100644
index 000000000..570a88a0e
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
@@ -0,0 +1,87 @@
+use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type;
+use clippy_utils::diagnostics::span_lint_and_help;
+use regex::Regex;
+use rustc_ast as ast;
+use rustc_hir::{Item, ItemKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// 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.
+ ///
+ /// ### Example
+ /// `Checks for use...` can be written as `Checks for usage...` .
+ pub ALMOST_STANDARD_LINT_FORMULATION,
+ internal,
+ "lint formulations must have a standardized format."
+}
+
+impl_lint_pass!(AlmostStandardFormulation => [ALMOST_STANDARD_LINT_FORMULATION]);
+
+pub struct AlmostStandardFormulation {
+ standard_formulations: Vec<StandardFormulations<'static>>,
+}
+
+#[derive(Debug)]
+struct StandardFormulations<'a> {
+ wrong_pattern: Regex,
+ correction: &'a str,
+}
+
+impl AlmostStandardFormulation {
+ pub fn new() -> Self {
+ let standard_formulations = vec![StandardFormulations {
+ wrong_pattern: Regex::new("^(Check for|Detects? uses?)").unwrap(),
+ correction: "Checks for",
+ }];
+ Self { standard_formulations }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let mut check_next = false;
+ if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
+ let lines = cx
+ .tcx
+ .hir()
+ .attrs(item.hir_id())
+ .iter()
+ .filter_map(|attr| ast::Attribute::doc_str(attr).map(|sym| (sym, attr)));
+ if is_lint_ref_type(cx, ty) {
+ for (line, attr) in lines {
+ let cur_line = line.as_str().trim();
+ if check_next && !cur_line.is_empty() {
+ for formulation in &self.standard_formulations {
+ let starts_with_correct_formulation = cur_line.starts_with(formulation.correction);
+ if !starts_with_correct_formulation && formulation.wrong_pattern.is_match(cur_line) {
+ if let Some(ident) = attr.ident() {
+ span_lint_and_help(
+ cx,
+ ALMOST_STANDARD_LINT_FORMULATION,
+ ident.span,
+ "non-standard lint formulation",
+ None,
+ &format!("try using `{}` instead", formulation.correction),
+ );
+ }
+ return;
+ }
+ }
+ return;
+ } else if cur_line.contains("What it does") {
+ check_next = true;
+ } else if cur_line.contains("Why is this bad") {
+ // Formulation documentation is done. Can add check to ensure that missing formulation is added
+ // and add a check if it matches no accepted formulation
+ return;
+ }
+ }
+ }
+ }
+ }
+}
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 f8978e30a..dced9fcf9 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
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
for def_id in def_path_def_ids(cx, module) {
- for item in cx.tcx.module_children(def_id).iter() {
+ for item in cx.tcx.module_children(def_id) {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
let ty = cx.tcx.type_of(item_def_id).subst_identity();
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 7a1cd3eff..107a62806 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
@@ -104,6 +104,8 @@ const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
/// The version that will be displayed if none has been defined
const VERSION_DEFAULT_STR: &str = "Unknown";
+const CHANGELOG_PATH: &str = "../CHANGELOG.md";
+
declare_clippy_lint! {
/// ### What it does
/// Collects metadata about clippy lints for the website.
@@ -195,8 +197,14 @@ This lint has the following configuration variables:
fn get_markdown_docs(&self) -> String {
format!(
- "## Lint Configuration Options\n| <div style=\"width:290px\">Option</div> | Default Value |\n|--|--|\n{}\n\n{}\n",
- self.configs_to_markdown(ClippyConfiguration::to_markdown_table_entry),
+ r#"# Lint Configuration Options
+
+The following list shows each configuration option, along with a description, its default value, an example
+and lints affected.
+
+---
+
+{}"#,
self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph),
)
}
@@ -254,6 +262,22 @@ Please use that command to update the file and do not edit it by hand.
self.get_markdown_docs(),
)
.unwrap();
+
+ // Write configuration links to CHANGELOG.md
+ let mut changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap();
+ let mut changelog_file = OpenOptions::new().read(true).write(true).open(CHANGELOG_PATH).unwrap();
+
+ if let Some(position) = changelog.find("<!-- begin autogenerated links to configuration documentation -->") {
+ // I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best
+ // we can do AFAIK.
+ changelog = changelog[..position].to_string();
+ }
+ writeln!(
+ changelog_file,
+ "{changelog}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
+ self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
+ )
+ .unwrap();
}
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index d3ea7cafa..fb0825693 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -12,6 +12,9 @@ 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
// ==================================================================
@@ -51,7 +54,7 @@ impl ClippyConfiguration {
#[cfg(feature = "internal")]
fn to_markdown_paragraph(&self) -> String {
format!(
- "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
+ "## `{}`\n{}\n\n**Default Value:** `{}` (`{}`)\n\n---\n**Affected lints:**\n{}\n\n",
self.name,
self.doc
.lines()
@@ -62,14 +65,13 @@ impl ClippyConfiguration {
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})"))
+ .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
.join("\n"),
)
}
-
#[cfg(feature = "internal")]
- fn to_markdown_table_entry(&self) -> String {
- format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
+ fn to_markdown_link(&self) -> String {
+ format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
}
}
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 7329e5081..2a594e750 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -1,26 +1,32 @@
+use std::ops::ControlFlow;
+
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
+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;
+use clippy_utils::{get_parent_expr, higher, is_trait_method};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind};
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;
#[expect(clippy::module_name_repetitions)]
-#[derive(Copy, Clone)]
+#[derive(Clone)]
pub struct UselessVec {
pub too_large_for_stack: u64,
+ pub msrv: Msrv,
}
declare_clippy_lint! {
/// ### What it does
- /// Checks for usage of `&vec![..]` when using `&[..]` would
+ /// Checks for usage of `vec![..]` when using `[..]` would
/// be possible.
///
/// ### Why is this bad?
@@ -46,16 +52,73 @@ declare_clippy_lint! {
impl_lint_pass!(UselessVec => [USELESS_VEC]);
+fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ matches!(cx.typeck_results().expr_ty_adjusted(e).kind(), ty::Ref(_, ty, _) if ty.is_slice())
+}
+
+/// Checks if the given expression is a method call to a `Vec` method
+/// that also exists on slices. If this returns true, it means that
+/// this expression does not actually require a `Vec` and could just work with an array.
+fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"];
+
+ if let ExprKind::MethodCall(path, ..) = e.kind {
+ ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str())
+ } else {
+ is_trait_method(cx, e, sym::IntoIterator)
+ }
+}
+
impl<'tcx> LateLintPass<'tcx> for UselessVec {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- // search for `&vec![_]` expressions where the adjusted type is `&[_]`
+ // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
if_chain! {
- if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
- if let ty::Slice(..) = ty.kind();
- if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
- if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
+ if adjusts_to_slice(cx, expr);
+ if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows());
then {
- self.check_vec_macro(cx, &vec_args, mutability, expr.span);
+ let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind {
+ // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.)
+ (SuggestedType::SliceRef(mutability), expr.span)
+ } else {
+ // `expr` is the `vec![_]` expansion, so suggest `[_]`
+ // and also use the span of the actual `vec![_]` expression
+ (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site)
+ };
+
+ self.check_vec_macro(cx, &vec_args, span, suggest_slice);
+ }
+ }
+
+ // search for `let foo = vec![_]` expressions where all uses of `foo`
+ // adjust to slices or call a method that exist on slices (e.g. len)
+ if let Some(vec_args) = higher::VecArgs::hir(cx, expr)
+ && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id)
+ // for now ignore locals with type annotations.
+ // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
+ && local.ty.is_none()
+ && let PatKind::Binding(_, id, ..) = local.pat.kind
+ && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr)))
+ {
+ let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
+ // allow indexing into a vec and some set of allowed method calls that exist on slices, too
+ if let Some(parent) = get_parent_expr(cx, expr)
+ && (adjusts_to_slice(cx, expr)
+ || matches!(parent.kind, ExprKind::Index(..))
+ || is_allowed_vec_method(cx, parent))
+ {
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::Break(())
+ }
+ }).is_continue();
+
+ if only_slice_uses {
+ self.check_vec_macro(
+ cx,
+ &vec_args,
+ expr.span.ctxt().outer_expn_data().call_site,
+ SuggestedType::Array
+ );
}
}
@@ -63,25 +126,36 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
if_chain! {
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
- if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
+ if self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR);
then {
// report the error around the `vec!` not inside `<std macros>:`
let span = arg.span.ctxt().outer_expn_data().call_site;
- self.check_vec_macro(cx, &vec_args, Mutability::Not, span);
+ self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array);
}
}
}
+
+ extract_msrv_attr!(LateContext);
+}
+
+#[derive(Copy, Clone)]
+enum SuggestedType {
+ /// Suggest using a slice `&[..]` / `&mut [..]`
+ SliceRef(Mutability),
+ /// Suggest using an array: `[..]`
+ Array,
}
impl UselessVec {
fn check_vec_macro<'tcx>(
- self,
+ &mut self,
cx: &LateContext<'tcx>,
vec_args: &higher::VecArgs<'tcx>,
- mutability: Mutability,
span: Span,
+ suggest_slice: SuggestedType,
) {
let mut applicability = Applicability::MachineApplicable;
+
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
@@ -90,21 +164,13 @@ impl UselessVec {
return;
}
- match mutability {
- Mutability::Mut => {
- format!(
- "&mut [{}; {}]",
- snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
- snippet_with_applicability(cx, len.span, "len", &mut applicability)
- )
- },
- Mutability::Not => {
- format!(
- "&[{}; {}]",
- snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
- snippet_with_applicability(cx, len.span, "len", &mut applicability)
- )
- },
+ let elem = snippet_with_applicability(cx, elem.span, "elem", &mut applicability);
+ let len = snippet_with_applicability(cx, len.span, "len", &mut applicability);
+
+ match suggest_slice {
+ SuggestedType::SliceRef(Mutability::Mut) => format!("&mut [{elem}; {len}]"),
+ SuggestedType::SliceRef(Mutability::Not) => format!("&[{elem}; {len}]"),
+ SuggestedType::Array => format!("[{elem}; {len}]"),
}
} else {
return;
@@ -116,22 +182,24 @@ impl UselessVec {
return;
}
let span = args[0].span.to(last.span);
+ let args = snippet_with_applicability(cx, span, "..", &mut applicability);
- match mutability {
- Mutability::Mut => {
- format!(
- "&mut [{}]",
- snippet_with_applicability(cx, span, "..", &mut applicability)
- )
+ match suggest_slice {
+ SuggestedType::SliceRef(Mutability::Mut) => {
+ format!("&mut [{args}]")
+ },
+ SuggestedType::SliceRef(Mutability::Not) => {
+ format!("&[{args}]")
},
- Mutability::Not => {
- format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
+ SuggestedType::Array => {
+ format!("[{args}]")
},
}
} else {
- match mutability {
- Mutability::Mut => "&mut []".into(),
- Mutability::Not => "&[]".into(),
+ match suggest_slice {
+ SuggestedType::SliceRef(Mutability::Mut) => "&mut []".to_owned(),
+ SuggestedType::SliceRef(Mutability::Not) => "&[]".to_owned(),
+ SuggestedType::Array => "[]".to_owned(),
}
}
},
@@ -142,7 +210,13 @@ impl UselessVec {
USELESS_VEC,
span,
"useless use of `vec!`",
- "you can use a slice directly",
+ &format!(
+ "you can use {} directly",
+ match suggest_slice {
+ SuggestedType::SliceRef(_) => "a slice",
+ SuggestedType::Array => "an array",
+ }
+ ),
snippet,
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
new file mode 100644
index 000000000..43248bccc
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -0,0 +1,131 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
+use rustc_ast::ast::{Item, VisibilityKind};
+use rustc_errors::Applicability;
+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::{symbol::kw, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `pub(self)` and `pub(in self)`.
+ ///
+ /// ### Why is this bad?
+ /// It's unnecessary, omitting the `pub` entirely will give the same results.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// pub(self) type OptBox<T> = Option<Box<T>>;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// type OptBox<T> = Option<Box<T>>;
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub NEEDLESS_PUB_SELF,
+ style,
+ "checks for usage of `pub(self)` and `pub(in self)`."
+}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `pub(<loc>)` with `in`.
+ ///
+ /// ### Why is this bad?
+ /// Consistency. Use it or don't, just be consistent about it.
+ ///
+ /// Also see the `pub_without_shorthand` lint for an alternative.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// pub(super) type OptBox<T> = Option<Box<T>>;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// pub(in super) type OptBox<T> = Option<Box<T>>;
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub PUB_WITH_SHORTHAND,
+ restriction,
+ "disallows usage of `pub(<loc>)`, without `in`"
+}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `pub(<loc>)` without `in`.
+ ///
+ /// Note: As you cannot write a module's path in `pub(<loc>)`, this will only trigger on
+ /// `pub(super)` and the like.
+ ///
+ /// ### Why is this bad?
+ /// Consistency. Use it or don't, just be consistent about it.
+ ///
+ /// Also see the `pub_with_shorthand` lint for an alternative.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// pub(in super) type OptBox<T> = Option<Box<T>>;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// pub(super) type OptBox<T> = Option<Box<T>>;
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub PUB_WITHOUT_SHORTHAND,
+ restriction,
+ "disallows usage of `pub(in <loc>)` with `in`"
+}
+declare_lint_pass!(Visibility => [NEEDLESS_PUB_SELF, PUB_WITH_SHORTHAND, PUB_WITHOUT_SHORTHAND]);
+
+impl EarlyLintPass for Visibility {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ 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) {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_PUB_SELF,
+ item.vis.span,
+ &format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }),
+ "remove it",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate)
+ && !*shorthand
+ && let [.., last] = &*path.segments
+ && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+ {
+ span_lint_and_sugg(
+ cx,
+ PUB_WITHOUT_SHORTHAND,
+ item.vis.span,
+ "usage of `pub` with `in`",
+ "remove it",
+ format!("pub({})", last.ident),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ if *shorthand
+ && let [.., last] = &*path.segments
+ && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+ {
+ span_lint_and_sugg(
+ cx,
+ PUB_WITH_SHORTHAND,
+ item.vis.span,
+ "usage of `pub` without `in`",
+ "add it",
+ format!("pub(in {})", last.ident),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> Option<bool> {
+ snippet_opt(cx, span).map(|s| !s.starts_with("pub"))
+}
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index a9089fba3..2a3d86988 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -65,8 +65,9 @@ declare_clippy_lint! {
/// This can lead to confusing error messages at best and to unexpected behavior at worst.
///
/// ### Exceptions
- /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
- /// provide modules named "prelude" specifically designed for wildcard import.
+ /// Wildcard imports are allowed from modules that their name contains `prelude`. Many crates
+ /// (including the standard library) provide modules named "prelude" specifically designed
+ /// for wildcard import.
///
/// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
///
@@ -159,7 +160,7 @@ impl LateLintPass<'_> for WildcardImports {
)
};
- let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false);
+ let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord();
let imports_string = if imports.len() == 1 {
imports.pop().unwrap()
} else if braced_glob {
@@ -212,7 +213,9 @@ impl WildcardImports {
// Allow "...prelude::..::*" imports.
// Many crates have a prelude, and it is imported as a glob by design.
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
- segments.iter().any(|ps| ps.ident.name == sym::prelude)
+ segments
+ .iter()
+ .any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str()))
}
// Allow "super::*" imports in tests.
diff --git a/src/tools/clippy/clippy_test_deps/Cargo.toml b/src/tools/clippy/clippy_test_deps/Cargo.toml
new file mode 100644
index 000000000..362c08e0d
--- /dev/null
+++ b/src/tools/clippy/clippy_test_deps/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "clippy_test_deps"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clippy_utils = { path = "../clippy_utils" }
+derive-new = "0.5"
+if_chain = "1.0"
+itertools = "0.10.1"
+quote = "1.0"
+serde = { version = "1.0.125", features = ["derive"] }
+syn = { version = "2.0", features = ["full"] }
+futures = "0.3"
+parking_lot = "0.12"
+tokio = { version = "1", features = ["io-util"] }
+regex = "1.5"
+clippy_lints = { path = "../clippy_lints" }
+
+[features]
+internal = ["clippy_lints/internal"]
diff --git a/src/tools/clippy/clippy_test_deps/src/lib.rs b/src/tools/clippy/clippy_test_deps/src/lib.rs
new file mode 100644
index 000000000..7d12d9af8
--- /dev/null
+++ b/src/tools/clippy/clippy_test_deps/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/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 66a5079fa..cfe686eb9 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
-version = "0.1.71"
+version = "0.1.72"
edition = "2021"
publish = false
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 9edaae853..c6d0b654f 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -12,25 +12,33 @@
//! 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::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
+use rustc_ast::{
+ ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy},
+ token::CommentKind,
+ AttrStyle,
+};
use rustc_hir::{
intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId,
- Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, Node, QPath, TraitItem,
- TraitItemKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
+ 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;
use rustc_session::Session;
-use rustc_span::{Span, Symbol};
+use rustc_span::{symbol::Ident, Span, Symbol};
use rustc_target::spec::abi::Abi;
/// The search pattern to look for. Used by `span_matches_pat`
-#[derive(Clone, Copy)]
+#[derive(Clone)]
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.
+ OwnedMultiStr(Vec<String>),
/// The string representation of the symbol.
Sym(Symbol),
/// Any decimal or hexadecimal digit depending on the location.
@@ -51,12 +59,16 @@ 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()),
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
})
@@ -271,14 +283,79 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
(start_pat, end_pat)
}
-pub trait WithSearchPat {
+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 {
+ // 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
+ },
+ AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
+ if matches!(attr.style, AttrStyle::Outer) {
+ (Pat::Str("///"), Pat::Str(""))
+ } else {
+ (Pat::Str("//!"), Pat::Str(""))
+ }
+ },
+ AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
+ if matches!(attr.style, AttrStyle::Outer) {
+ (Pat::Str("/**"), Pat::Str("*/"))
+ } else {
+ (Pat::Str("/*!"), Pat::Str("*/"))
+ }
+ },
+ }
+}
+
+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::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,
+ ),
+ TyKind::Never => (Pat::Str("!"), Pat::Str("")),
+ TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")),
+ TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
+ TyKind::Path(qpath) => qpath_search_pat(&qpath),
+ // NOTE: This is missing `TraitObject`. It 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);
fn span(&self) -> Span;
}
macro_rules! impl_with_search_pat {
($cx:ident: $ty:ident with $fn:ident $(($tcx:ident))?) => {
- impl<'cx> WithSearchPat for $ty<'cx> {
+ impl<'cx> WithSearchPat<'cx> for $ty<'cx> {
type Context = $cx<'cx>;
#[allow(unused_variables)]
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
@@ -297,8 +374,9 @@ impl_with_search_pat!(LateContext: TraitItem with trait_item_search_pat);
impl_with_search_pat!(LateContext: ImplItem with impl_item_search_pat);
impl_with_search_pat!(LateContext: FieldDef with field_def_search_pat);
impl_with_search_pat!(LateContext: Variant with variant_search_pat);
+impl_with_search_pat!(LateContext: Ty with ty_search_pat);
-impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
+impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
type Context = LateContext<'cx>;
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
@@ -310,11 +388,37 @@ impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
}
}
+// `Attribute` does not have the `hir` associated lifetime, so we cannot use the macro
+impl<'cx> WithSearchPat<'cx> for &'cx Attribute {
+ type Context = LateContext<'cx>;
+
+ fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
+ attr_search_pat(self)
+ }
+
+ fn span(&self) -> Span {
+ self.span
+ }
+}
+
+// `Ident` does not have the `hir` associated lifetime, so we cannot use the macro
+impl<'cx> WithSearchPat<'cx> for Ident {
+ type Context = LateContext<'cx>;
+
+ fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
+ ident_search_pat(*self)
+ }
+
+ fn span(&self) -> Span {
+ self.span
+ }
+}
+
/// Checks if the item likely came from a proc-macro.
///
/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
/// it is significantly slower than both of those.
-pub fn is_from_proc_macro<T: WithSearchPat>(cx: &T::Context, item: &T) -> bool {
+pub fn is_from_proc_macro<'cx, T: WithSearchPat<'cx>>(cx: &T::Context, item: &T) -> bool {
let (start_pat, end_pat) = item.search_pat(cx);
!span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
}
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index fb772644c..d1cfdc496 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -6,7 +6,7 @@ use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
+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;
@@ -14,7 +14,7 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
use rustc_middle::ty::{List, SubstsRef};
use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
use rustc_span::SyntaxContext;
use std::cmp::Ordering::{self, Equal};
use std::hash::{Hash, Hasher};
@@ -22,7 +22,8 @@ use std::iter;
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
-pub enum Constant {
+pub enum Constant<'tcx> {
+ Adt(rustc_middle::mir::ConstantKind<'tcx>),
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
@@ -38,20 +39,20 @@ pub enum Constant {
/// `true` or `false`.
Bool(bool),
/// An array of constants.
- Vec(Vec<Constant>),
+ Vec(Vec<Constant<'tcx>>),
/// Also an array, but with only one constant, repeated N times.
- Repeat(Box<Constant>, u64),
+ Repeat(Box<Constant<'tcx>>, u64),
/// A tuple of constants.
- Tuple(Vec<Constant>),
+ Tuple(Vec<Constant<'tcx>>),
/// A raw pointer.
RawPtr(u128),
/// A reference
- Ref(Box<Constant>),
+ Ref(Box<Constant<'tcx>>),
/// A literal with syntax error.
Err,
}
-impl PartialEq for Constant {
+impl<'tcx> PartialEq for Constant<'tcx> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Str(ls), Self::Str(rs)) => ls == rs,
@@ -80,13 +81,16 @@ impl PartialEq for Constant {
}
}
-impl Hash for Constant {
+impl<'tcx> Hash for Constant<'tcx> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
std::mem::discriminant(self).hash(state);
match *self {
+ Self::Adt(ref elem) => {
+ elem.hash(state);
+ },
Self::Str(ref s) => {
s.hash(state);
},
@@ -126,7 +130,7 @@ impl Hash for Constant {
}
}
-impl Constant {
+impl<'tcx> Constant<'tcx> {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
@@ -209,7 +213,7 @@ impl Constant {
}
/// Parses a `LitKind` to a `Constant`.
-pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
+pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constant<'tcx> {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
@@ -248,7 +252,7 @@ pub fn constant<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<Constant> {
+) -> Option<Constant<'tcx>> {
ConstEvalLateContext::new(lcx, typeck_results).expr(e)
}
@@ -257,7 +261,7 @@ pub fn constant_with_source<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<(Constant, ConstantSource)> {
+) -> Option<(Constant<'tcx>, ConstantSource)> {
let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results);
let res = ctxt.expr(e);
res.map(|x| (x, ctxt.source))
@@ -268,7 +272,7 @@ pub fn constant_simple<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<Constant> {
+) -> Option<Constant<'tcx>> {
constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
}
@@ -338,8 +342,10 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
/// Simple constant folding: Insert an expression, get a constant or none.
- pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
+ pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind {
+ ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
+ ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(lit) => {
@@ -392,13 +398,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
},
ExprKind::Index(arr, index) => self.index(arr, index),
ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
- // TODO: add other expressions.
+ ExprKind::Field(local_expr, ref field) => {
+ let result = self.expr(local_expr);
+ if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
+ && let ty::Adt(adt_def, _) = constant.ty().kind()
+ && 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 {
+ result
+ }
+ },
_ => None,
}
}
#[expect(clippy::cast_possible_wrap)]
- fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
use self::Constant::{Bool, Int};
match *o {
Bool(b) => Some(Bool(!b)),
@@ -414,7 +432,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
@@ -433,28 +451,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part.
- fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
+ fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
}
/// Lookup a possibly constant expression from an `ExprKind::Path`.
- fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
+ fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant<'tcx>> {
let res = self.typeck_results.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
// 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) &&
- is_direct_expn_of(span, "cfg").is_some() {
+ 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)
+ && is_direct_expn_of(*span, "cfg").is_some()
+ {
return None;
}
@@ -462,25 +477,23 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let substs = if self.substs.is_empty() {
substs
} else {
- EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
+ EarlyBinder::bind(substs).subst(self.lcx.tcx, self.substs)
};
-
let result = self
.lcx
.tcx
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None)
.ok()
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
- let result = miri_to_const(self.lcx.tcx, result)?;
+ let result = miri_to_const(self.lcx, result)?;
self.source = ConstantSource::Constant;
Some(result)
},
- // FIXME: cover all usable cases.
_ => None,
}
}
- fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
+ fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
let lhs = self.expr(lhs);
let index = self.expr(index);
@@ -506,7 +519,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
/// A block can only yield a constant if it only has one constant expression.
- fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
+ fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if block.stmts.is_empty()
&& let Some(expr) = block.expr
{
@@ -539,7 +552,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
+ fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
if let Some(Constant::Bool(b)) = self.expr(cond) {
if b {
self.expr(then)
@@ -551,7 +564,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
+ fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
@@ -644,23 +657,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
-pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant> {
+pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant<'tcx>> {
use rustc_middle::mir::interpret::ConstValue;
match result {
- mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => {
- match result.ty().kind() {
- ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
- ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
- ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
- int.try_into().expect("invalid f32 bit representation"),
- ))),
- ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
- int.try_into().expect("invalid f64 bit representation"),
- ))),
- ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
- // FIXME: implement other conversions.
- _ => None,
- }
+ mir::ConstantKind::Val(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()))),
+ ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
+ int.try_into().expect("invalid f32 bit representation"),
+ ))),
+ ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
+ int.try_into().expect("invalid f64 bit representation"),
+ ))),
+ ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
+ _ => None,
},
mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
ty::Ref(_, tam, _) => match tam.kind() {
@@ -676,35 +687,54 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
_ => None,
},
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => 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.kind().try_to_target_usize(tcx) {
+ 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>>>()
+ .collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
- ty::Float(FloatTy::F64) => match len.kind().try_to_target_usize(tcx) {
+ 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>>>()
+ .collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
- // FIXME: implement other array type conversions.
_ => None,
},
_ => None,
},
- // FIXME: implement other conversions.
_ => None,
}
}
+
+fn field_of_struct<'tcx>(
+ adt_def: ty::AdtDef<'tcx>,
+ lcx: &LateContext<'tcx>,
+ result: mir::ConstantKind<'tcx>,
+ field: &Ident,
+) -> Option<mir::ConstantKind<'tcx>> {
+ if let mir::ConstantKind::Val(result, ty) = result
+ && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics((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::ConstantKind::Val(val, ty))
+ }
+ else {
+ None
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 812f6fe71..edd87546a 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -46,7 +46,7 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
/// | ^^^^^^^^^^^^^^^^^^^^^^^
/// ```
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
- cx.struct_span_lint(lint, sp, msg, |diag| {
+ cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
docs_link(diag, lint);
diag
});
@@ -80,11 +80,12 @@ pub fn span_lint_and_help<T: LintContext>(
help_span: Option<Span>,
help: &str,
) {
- cx.struct_span_lint(lint, span, msg, |diag| {
+ cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
+ let help = help.to_string();
if let Some(help_span) = help_span {
- diag.span_help(help_span, help);
+ diag.span_help(help_span, help.to_string());
} else {
- diag.help(help);
+ diag.help(help.to_string());
}
docs_link(diag, lint);
diag
@@ -122,7 +123,8 @@ pub fn span_lint_and_note<T: LintContext>(
note_span: Option<Span>,
note: &str,
) {
- cx.struct_span_lint(lint, span, msg, |diag| {
+ cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
+ let note = note.to_string();
if let Some(note_span) = note_span {
diag.span_note(note_span, note);
} else {
@@ -143,7 +145,7 @@ where
S: Into<MultiSpan>,
F: FnOnce(&mut Diagnostic),
{
- cx.struct_span_lint(lint, sp, msg, |diag| {
+ cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
f(diag);
docs_link(diag, lint);
diag
@@ -151,7 +153,7 @@ where
}
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
- cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
docs_link(diag, lint);
diag
});
@@ -165,7 +167,7 @@ pub fn span_lint_hir_and_then(
msg: &str,
f: impl FnOnce(&mut Diagnostic),
) {
- cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
f(diag);
docs_link(diag, lint);
diag
@@ -202,7 +204,7 @@ pub fn span_lint_and_sugg<T: LintContext>(
applicability: Applicability,
) {
span_lint_and_then(cx, lint, sp, msg, |diag| {
- diag.span_suggestion(sp, help, sugg, applicability);
+ diag.span_suggestion(sp, help.to_string(), sugg, applicability);
});
}
@@ -232,5 +234,5 @@ pub fn multispan_sugg_with_applicability<I>(
) where
I: IntoIterator<Item = (Span, String)>,
{
- diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
+ diag.multipart_suggestion(help_msg.to_string(), sugg.into_iter().collect(), applicability);
}
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index 3df40942e..4a845ca63 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -15,7 +15,8 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, PredicateKind};
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::{sym, Symbol};
use std::cmp;
use std::ops;
@@ -73,7 +74,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
.flat_map(|v| v.fields.iter())
.any(|x| matches!(cx.tcx.type_of(x.did).subst_identity().peel_refs().kind(), ty::Param(_)))
&& all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(pred)) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+ ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
_ => true,
})
&& subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
@@ -114,6 +115,20 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
if self.eagerness == ForceNoChange {
return;
}
+
+ // Autoderef through a user-defined `Deref` impl can have side-effects,
+ // so don't suggest changing it.
+ if self
+ .cx
+ .typeck_results()
+ .expr_adjustments(e)
+ .iter()
+ .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_))))
+ {
+ self.eagerness |= NoChange;
+ return;
+ }
+
match e.kind {
ExprKind::Call(
&Expr {
@@ -173,11 +188,15 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
self.eagerness = Lazy;
}
},
-
+ // Custom `Deref` impl might have side effects
+ ExprKind::Unary(UnOp::Deref, e)
+ if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() =>
+ {
+ self.eagerness |= NoChange;
+ },
// Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
-
ExprKind::Unary(_, e)
if matches!(
self.cx.typeck_results().expr_ty(e).kind(),
@@ -191,6 +210,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
ExprKind::Break(..)
| ExprKind::Continue(_)
| ExprKind::Ret(_)
+ | ExprKind::Become(_)
| ExprKind::InlineAsm(_)
| ExprKind::Yield(..)
| ExprKind::Err(_) => {
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a49246a78..3e1d73564 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -845,6 +845,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e);
}
},
+ ExprKind::Become(f) => {
+ self.hash_expr(f);
+ },
ExprKind::Path(ref qpath) => {
self.hash_qpath(qpath);
},
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 8c883445a..727b59f1f 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -20,6 +20,7 @@
extern crate rustc_ast;
extern crate rustc_ast_pretty;
extern crate rustc_attr;
+extern crate rustc_const_eval;
extern crate rustc_data_structures;
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
#[allow(unused_extern_crates)]
@@ -326,6 +327,18 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
}
+/// Checks if the `def_id` belongs to a function that is part of a trait impl.
+pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
+ if let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(def_id)
+ && let Node::Item(item) = cx.tcx.hir().get_parent(hir_id)
+ && let ItemKind::Impl(imp) = item.kind
+ {
+ imp.of_trait.is_some()
+ } else {
+ false
+ }
+}
+
/// Checks if the given expression is a path referring an item on the trait
/// that is marked with the given diagnostic item.
///
@@ -1498,7 +1511,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& 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 = ConstantKind::from_value(const_val, bnd_ty)
- && let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
+ && let Some(min_const) = miri_to_const(cx, min_const_kind)
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
{
start_const == min_const
@@ -1514,7 +1527,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& 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 = ConstantKind::from_value(const_val, bnd_ty)
- && let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
+ && 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
@@ -2396,7 +2409,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol
/// Checks if the function containing the given `HirId` is a `#[test]` function
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
with_test_item_names(tcx, tcx.parent_module(id), |names| {
tcx.hir()
@@ -2418,7 +2431,7 @@ 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
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
+/// 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 {
if attr.has_name(sym::cfg)
@@ -2440,7 +2453,7 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
/// Checks whether item either has `test` attribute applied, or
/// is a module with `test` in its name.
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
is_in_test_function(tcx, item.hir_id())
|| matches!(item.kind, ItemKind::Mod(..))
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index e4a4936ff..00f3abaec 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -9,7 +9,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
use rustc_lint::LateContext;
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
use std::cell::RefCell;
use std::ops::ControlFlow;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -415,8 +415,18 @@ pub fn find_format_arg_expr<'hir, 'ast>(
start: &'hir Expr<'hir>,
target: &'ast FormatArgument,
) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> {
+ let SpanData {
+ lo,
+ hi,
+ ctxt,
+ parent: _,
+ } = target.expr.span.data();
+
for_each_expr(start, |expr| {
- if expr.span == target.expr.span {
+ // When incremental compilation is enabled spans gain a parent during AST to HIR lowering,
+ // since we're comparing an AST span to a HIR one we need to ignore the parent field
+ let data = expr.span.data();
+ if data.lo == lo && data.hi == hi && data.ctxt == ctxt {
ControlFlow::Break(expr)
} else {
ControlFlow::Continue(())
diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs
index 26c0015e8..131f3c0aa 100644
--- a/src/tools/clippy/clippy_utils/src/mir/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs
@@ -101,21 +101,26 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
/// Returns the `mir::Body` containing the node associated with `hir_id`.
#[allow(clippy::module_name_repetitions)]
-pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
+pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> {
let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
- tcx.optimized_mir(body_owner_local_def_id.to_def_id())
+ if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
+ Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id()))
+ } else {
+ None
+ }
}
/// Tries to determine the `Local` corresponding to `expr`, if any.
/// This function is expensive and should be used sparingly.
pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
- let mir = enclosing_mir(tcx, expr.hir_id);
- mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
- if local_decl.source_info.span == expr.span {
- Some(local)
- } else {
- None
- }
+ enclosing_mir(tcx, expr.hir_id).and_then(|mir| {
+ mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
+ if local_decl.source_info.span == expr.span {
+ Some(local)
+ } else {
+ None
+ }
+ })
})
}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index e05de2dc9..3637476c4 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -19,8 +19,10 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
+ 1,71,0 { TUPLE_ARRAY_CONVERSIONS }
+ 1,70,0 { OPTION_IS_SOME_AND }
1,68,0 { PATH_MAIN_SEPARATOR_STR }
- 1,65,0 { LET_ELSE }
+ 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
1,55,0 { SEEK_REWIND }
@@ -28,7 +30,7 @@ msrv_aliases! {
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,50,0 { BOOL_THEN, CLAMP }
- 1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
+ 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2 }
@@ -42,11 +44,13 @@ msrv_aliases! {
1,34,0 { TRY_FROM }
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
1,28,0 { FROM_BOOL }
+ 1,27,0 { ITERATOR_TRY_FOLD }
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
1,24,0 { IS_ASCII_DIGIT }
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
+ 1,15,0 { MAYBE_BOUND_IN_WHERE }
}
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
index c225398ad..bbe4149fe 100644
--- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs
+++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
@@ -161,7 +161,7 @@ impl<'a> NumericLiteral<'a> {
}
if let Some((separator, exponent)) = self.exponent {
- if exponent != "0" {
+ if !exponent.is_empty() && exponent != "0" {
output.push_str(separator);
Self::group_digits(&mut output, exponent, group_size, true, false);
}
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 0f0792fda..0e6f01287 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -15,7 +15,6 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
];
#[cfg(feature = "internal")]
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
-pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
@@ -93,7 +92,6 @@ pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
-pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
@@ -125,8 +123,6 @@ pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
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_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
-pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
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")]
@@ -163,3 +159,5 @@ pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
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"];
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 c0d2c835d..fbf4ab272 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
@@ -4,52 +4,30 @@
// differ from the time of `rustc` even if the name stays the same.
use crate::msrvs::Msrv;
+use hir::LangItem;
+use rustc_const_eval::transform::check_consts::ConstCx;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::traits::Obligation;
use rustc_middle::mir::{
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
Terminator, TerminatorKind,
};
+use rustc_middle::traits::{ImplSource, ObligationCause};
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
+use rustc_middle::ty::{BoundConstness, TraitRef};
use rustc_semver::RustcVersion;
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
let def_id = body.source.def_id();
- let mut current = def_id;
- loop {
- let predicates = tcx.predicates_of(current);
- for (predicate, _) in predicates.predicates {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(
- ty::Clause::RegionOutlives(_)
- | ty::Clause::TypeOutlives(_)
- | ty::Clause::Projection(_)
- | ty::Clause::Trait(..)
- | ty::Clause::ConstArgHasType(..),
- )
- | ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
- ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"),
- ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
- ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
- ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"),
- ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"),
- ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"),
- }
- }
- match predicates.parent {
- Some(parent) => current = parent,
- None => break,
- }
- }
for local in &body.local_decls {
check_ty(tcx, local.ty, local.source_info.span)?;
@@ -61,7 +39,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
body.local_decls.iter().next().unwrap().source_info.span,
)?;
- for bb in body.basic_blocks.iter() {
+ for bb in &*body.basic_blocks {
check_terminator(tcx, body, bb.terminator(), msrv)?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt)?;
@@ -89,7 +67,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
return Err((span, "function pointers in const fn are unstable".into()));
},
ty::Dynamic(preds, _, _) => {
- for pred in preds.iter() {
+ for pred in *preds {
match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
return Err((
@@ -141,18 +119,18 @@ fn check_rvalue<'tcx>(
| CastKind::FloatToFloat
| CastKind::FnPtrToPtr
| CastKind::PtrToPtr
- | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
+ | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
operand,
_,
) => check_operand(tcx, operand, span, body),
Rvalue::Cast(
- CastKind::Pointer(
- PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
+ CastKind::PointerCoercion(
+ PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::ReifyFnPointer,
),
_,
_,
) => Err((span, "function pointer casts are not allowed in const fn".into())),
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
+ Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
deref_ty.ty
} else {
@@ -256,7 +234,19 @@ fn check_statement<'tcx>(
fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
match operand {
- Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
+ Operand::Move(place) => {
+ if !place.projection.as_ref().is_empty()
+ && !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body)
+ {
+ return Err((
+ span,
+ "cannot drop locals with a non constant destructor in const fn".into(),
+ ));
+ }
+
+ check_place(tcx, *place, span, body)
+ },
+ Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) {
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
None => Ok(()),
@@ -265,12 +255,10 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
}
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
- let mut cursor = place.projection.as_ref();
- while let [ref proj_base @ .., elem] = *cursor {
- cursor = proj_base;
+ for (base, elem) in place.as_ref().iter_projections() {
match elem {
ProjectionElem::Field(..) => {
- let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
+ let base_ty = base.ty(body, tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No union field accesses in `const fn`
if def.is_union() {
@@ -305,19 +293,23 @@ fn check_terminator<'tcx>(
| TerminatorKind::Resume
| TerminatorKind::Terminate
| TerminatorKind::Unreachable => Ok(()),
-
- TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
-
+ TerminatorKind::Drop { place, .. } => {
+ if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
+ return Err((
+ span,
+ "cannot drop locals with a non constant destructor in const fn".into(),
+ ));
+ }
+ Ok(())
+ },
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
-
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into()))
},
-
TerminatorKind::Call {
func,
args,
- from_hir_call: _,
+ call_source: _,
destination: _,
target: _,
unwind: _,
@@ -357,7 +349,6 @@ fn check_terminator<'tcx>(
Err((span, "can only call other const fns within const fn".into()))
}
},
-
TerminatorKind::Assert {
cond,
expected: _,
@@ -365,7 +356,6 @@ fn check_terminator<'tcx>(
target: _,
unwind: _,
} => check_operand(tcx, cond, span, body),
-
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
}
}
@@ -379,8 +369,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
// 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.
+ // doesn't accept the `-dev` version number so we have to strip it off.
let short_version = since
.as_str()
.split('-')
@@ -398,3 +387,35 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
}
})
}
+
+#[expect(clippy::similar_names)] // bit too pedantic
+fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
+ // Avoid selecting for simple cases, such as builtin types.
+ if ty::util::is_trivially_const_drop(ty) {
+ return true;
+ }
+
+ let obligation = Obligation::new(
+ tcx,
+ ObligationCause::dummy_with_span(body.span),
+ ConstCx::new(tcx, body).param_env.with_const(),
+ TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]).with_constness(BoundConstness::ConstIfConst),
+ );
+
+ let infcx = tcx.infer_ctxt().build();
+ let mut selcx = SelectionContext::new(&infcx);
+ let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
+ return false;
+ };
+
+ if !matches!(
+ impl_src,
+ ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+ ) {
+ return false;
+ }
+
+ let ocx = ObligationCtxt::new(&infcx);
+ ocx.register_obligations(impl_src.nested_obligations());
+ ocx.select_all_or_error().is_empty()
+}
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 0f6029064..582337b47 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -4,7 +4,7 @@
use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext};
use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap};
@@ -71,11 +71,16 @@ 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 {
- format!("{{ {code} }}")
- } else if let ExprKind::Block(_, _) = expr.kind {
+ if !from_macro &&
+ let ExprKind::Block(block, _) = expr.kind &&
+ block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+ {
format!("{code}")
} else {
+ // FIXME: add extra indent for the unsafe blocks:
+ // original code: unsafe { ... }
+ // result code: { unsafe { ... } }
+ // desired code: {\n unsafe { ... }\n}
format!("{{ {code} }}")
}
}
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 14f7f0301..cf781e18c 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -147,6 +147,7 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Path(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::Ret(..)
+ | hir::ExprKind::Become(..)
| hir::ExprKind::Struct(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)),
@@ -211,6 +212,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Path(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Become(..)
| ast::ExprKind::Yeet(..)
| ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Struct(..)
@@ -741,7 +743,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
if let Some(indent) = indentation(cx, item) {
let span = item.with_hi(item.lo());
- self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability);
+ self.span_suggestion(span, msg.to_string(), format!("{attr}\n{indent}"), applicability);
}
}
@@ -762,7 +764,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
})
.collect::<String>();
- self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability);
+ self.span_suggestion(span, msg.to_string(), format!("{new_item}\n{indent}"), applicability);
}
}
@@ -779,7 +781,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
}
}
- self.span_suggestion(remove_span, msg, "", applicability);
+ self.span_suggestion(remove_span, msg.to_string(), "", applicability);
}
}
@@ -942,7 +944,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
},
// item is used in a call
// i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
- ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [call_args @ ..], _) => {
+ ExprKind::Call(_, call_args) | ExprKind::MethodCall(_, _, call_args, _) => {
let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 7b4ed77e8..d650cbe0b 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -17,8 +17,8 @@ use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv,
- Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
- TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
+ Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+ UintTy, VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident;
@@ -94,7 +94,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
match predicate.kind().skip_binder() {
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
// and check substitutions to find `U`.
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::ClauseKind::Trait(trait_predicate) => {
if trait_predicate
.trait_ref
.substs
@@ -107,7 +107,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
},
// For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
// so we check the term for `U`.
- ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
+ ty::ClauseKind::Projection(projection_predicate) => {
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
return true;
@@ -268,7 +268,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
+ if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
return true;
}
@@ -277,7 +277,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
false
},
ty::Dynamic(binder, _, _) => {
- for predicate in binder.iter() {
+ for predicate in *binder {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
return true;
@@ -563,7 +563,7 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
}
/// Gets an iterator over all predicates which apply to the given item.
-pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(ty::Clause<'_>, Span)> {
let mut next_id = Some(id);
iter::from_fn(move || {
next_id.take().map(|id| {
@@ -665,7 +665,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => sig_from_bounds(
cx,
ty,
- cx.tcx.item_bounds(def_id).subst(cx.tcx, substs),
+ cx.tcx.item_bounds(def_id).subst_iter(cx.tcx, substs),
cx.tcx.opt_parent(def_id),
),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
@@ -698,7 +698,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
fn sig_from_bounds<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
- predicates: &'tcx [Predicate<'tcx>],
+ predicates: impl IntoIterator<Item = ty::Clause<'tcx>>,
predicates_id: Option<DefId>,
) -> Option<ExprFnSig<'tcx>> {
let mut inputs = None;
@@ -707,7 +707,7 @@ fn sig_from_bounds<'tcx>(
for pred in predicates {
match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(p))
+ ty::ClauseKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id()))
@@ -720,7 +720,7 @@ fn sig_from_bounds<'tcx>(
}
inputs = Some(i);
},
- PredicateKind::Clause(ty::Clause::Projection(p))
+ ty::ClauseKind::Projection(p)
if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty =>
{
if output.is_some() {
@@ -747,7 +747,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
.subst_iter_copied(cx.tcx, ty.substs)
{
match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(p))
+ ty::ClauseKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
@@ -760,9 +760,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
}
inputs = Some(i);
},
- PredicateKind::Clause(ty::Clause::Projection(p))
- if Some(p.projection_ty.def_id) == lang_items.fn_once_output() =>
- {
+ ty::ClauseKind::Projection(p) if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => {
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
@@ -937,7 +935,7 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<
}
/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
-pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
+pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool {
let ty::Param(ty) = *ty.kind() else {
return false;
};
@@ -950,7 +948,7 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc
predicates
.iter()
.try_fold(false, |found, p| {
- if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder()
+ 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
{
@@ -1126,7 +1124,7 @@ pub fn make_normalized_projection<'tcx>(
);
return None;
}
- match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.def_id, ty.substs)) {
+ match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx,ty.def_id, ty.substs)) {
Ok(ty) => Some(ty),
Err(e) => {
debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
@@ -1180,3 +1178,51 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
_ => false,
}
}
+
+pub fn make_normalized_projection_with_regions<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ container_id: DefId,
+ assoc_ty: Symbol,
+ substs: impl IntoIterator<Item = impl Into<GenericArg<'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, subst)) = ty
+ .substs
+ .iter()
+ .enumerate()
+ .find(|(_, subst)| subst.has_late_bound_regions())
+ {
+ debug_assert!(
+ false,
+ "substs contain late-bound region at index `{i}` which can't be normalized.\n\
+ use `TyCtxt::erase_late_bound_regions`\n\
+ note: subst is `{subst:#?}`",
+ );
+ return None;
+ }
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ match tcx
+ .infer_ctxt()
+ .build()
+ .at(&cause, param_env)
+ .query_normalize(Ty::new_projection(tcx,ty.def_id, ty.substs))
+ {
+ Ok(ty) => Some(ty.value),
+ Err(e) => {
+ debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
+ None
+ },
+ }
+ }
+ helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
+}
+
+pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) {
+ Ok(ty) => ty.value,
+ Err(_) => ty,
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index ab3976a13..985508521 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -82,7 +82,7 @@ pub struct ParamBindingIdCollector {
impl<'tcx> ParamBindingIdCollector {
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
let mut hir_ids: Vec<hir::HirId> = Vec::new();
- for param in body.params.iter() {
+ for param in body.params {
let mut finder = ParamBindingIdCollector {
binding_hir_ids: Vec::new(),
};
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 5dcd71cef..8dafa723a 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -651,6 +651,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
// Either drops temporaries, jumps out of the current expression, or has no sub expression.
ExprKind::DropTemps(_)
| ExprKind::Ret(_)
+ | ExprKind::Become(_)
| ExprKind::Break(..)
| ExprKind::Yield(..)
| ExprKind::Block(..)
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 139102798..4dc906d00 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.71"
+version = "0.1.72"
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 5232e4ab7..0057256f6 100644
--- a/src/tools/clippy/declare_clippy_lint/src/lib.rs
+++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs
@@ -85,8 +85,8 @@ impl Parse for ClippyLint {
/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
-/// 4. The `description` that contains a short explanation on what's wrong with code where the
-/// lint is triggered.
+/// 4. The `description` that contains a short explanation on what's wrong with code where the lint
+/// is triggered.
///
/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
/// enabled by default. As said in the README.md of this repository, if the lint level mapping
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 27d32f390..a828d1237 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -22,7 +22,7 @@ rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
tar = "0.4"
-toml = "0.5"
+toml = "0.7.3"
ureq = "2.2"
walkdir = "2.3"
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 03d1877d6..de56a6f82 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -474,7 +474,7 @@ fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
});
} else if let Some(ref versions) = tk.versions {
// if we have multiple versions, save each one
- for ver in versions.iter() {
+ for ver in versions {
crate_sources.push(CrateSource::CratesIo {
name: tk.name.clone(),
version: ver.to_string(),
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index bc7fb711e..4475d914c 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-05-20"
+channel = "nightly-2023-06-29"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml
index 10d397620..18b2a3346 100644
--- a/src/tools/clippy/rustfmt.toml
+++ b/src/tools/clippy/rustfmt.toml
@@ -5,3 +5,4 @@ wrap_comments = true
edition = "2021"
error_on_line_overflow = true
version = "Two"
+ignore = ["tests/ui/crashes/ice-10912.rs"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 59bf447a7..1eb288b15 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -16,7 +16,9 @@ extern crate rustc_session;
extern crate rustc_span;
use rustc_interface::interface;
+use rustc_session::config::ErrorOutputType;
use rustc_session::parse::ParseSess;
+use rustc_session::EarlyErrorHandler;
use rustc_span::symbol::Symbol;
use std::env;
@@ -70,7 +72,7 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>)
/// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy
/// when any of them are modified
-fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
+fn track_files(parse_sess: &mut ParseSess) {
let file_depinfo = parse_sess.file_depinfo.get_mut();
// Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver`
@@ -79,10 +81,7 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
file_depinfo.insert(Symbol::intern("Cargo.toml"));
}
- // `clippy.toml`
- if let Some(path) = conf_path_string {
- file_depinfo.insert(Symbol::intern(&path));
- }
+ // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()`
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
// it is rebuilt
@@ -126,17 +125,11 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
#[allow(rustc::bad_opt_access)]
fn config(&mut self, config: &mut interface::Config) {
let conf_path = clippy_lints::lookup_conf_file();
- let conf_path_string = if let Ok((Some(path), _)) = &conf_path {
- path.to_str().map(String::from)
- } else {
- None
- };
-
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| {
track_clippy_args(parse_sess, &clippy_args_var);
- track_files(parse_sess, conf_path_string);
+ track_files(parse_sess);
}));
config.register_lints = Some(Box::new(move |sess, lint_store| {
// technically we're ~guaranteed that this is none but might as well call anything that
@@ -196,7 +189,9 @@ const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/ne
#[allow(clippy::too_many_lines)]
pub fn main() {
- rustc_driver::init_rustc_env_logger();
+ let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+
+ rustc_driver::init_rustc_env_logger(&handler);
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
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 188ff87ab..cdc85cb33 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -6,7 +6,7 @@ use std::env;
use std::path::PathBuf;
use std::process::{self, Command};
-const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
+const CARGO_CLIPPY_HELP: &str = "Checks a package to catch common mistakes and improve your Rust code.
Usage:
cargo clippy [options] [--] [<opts>...]
@@ -31,7 +31,7 @@ with:
You can use tool lints to allow or deny lints from your code, e.g.:
#[allow(clippy::needless_lifetimes)]
-"#;
+";
fn show_help() {
println!("{CARGO_CLIPPY_HELP}");
@@ -57,7 +57,9 @@ pub fn main() {
if let Some(pos) = env::args().position(|a| a == "--explain") {
if let Some(mut lint) = env::args().nth(pos + 1) {
lint.make_ascii_lowercase();
- clippy_lints::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"));
+ process::exit(clippy_lints::explain(
+ &lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"),
+ ));
} else {
show_help();
}
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 35d75cc51..0fd37c640 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -4,16 +4,14 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
-use compiletest_rs as compiletest;
-use compiletest_rs::common::Mode as TestMode;
+use compiletest::{status_emitter, CommandBuilder};
+use ui_test as compiletest;
+use ui_test::Mode as TestMode;
-use std::collections::HashMap;
use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
-use std::io;
use std::path::{Path, PathBuf};
-use std::sync::LazyLock;
use test_utils::IS_RUSTC_TEST_SUITE;
mod test_utils;
@@ -21,143 +19,41 @@ mod test_utils;
// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
-/// All crates used in UI tests are listed here
-static TEST_DEPENDENCIES: &[&str] = &[
- "clippy_lints",
- "clippy_utils",
- "derive_new",
- "futures",
- "if_chain",
- "itertools",
- "quote",
- "regex",
- "serde",
- "serde_derive",
- "syn",
- "tokio",
- "parking_lot",
- "rustc_semver",
-];
-
-// Test dependencies may need an `extern crate` here to ensure that they show up
-// in the depinfo file (otherwise cargo thinks they are unused)
-#[allow(unused_extern_crates)]
-extern crate clippy_lints;
-#[allow(unused_extern_crates)]
-extern crate clippy_utils;
-#[allow(unused_extern_crates)]
-extern crate derive_new;
-#[allow(unused_extern_crates)]
-extern crate futures;
-#[allow(unused_extern_crates)]
-extern crate if_chain;
-#[allow(unused_extern_crates)]
-extern crate itertools;
-#[allow(unused_extern_crates)]
-extern crate parking_lot;
-#[allow(unused_extern_crates)]
-extern crate quote;
-#[allow(unused_extern_crates)]
-extern crate rustc_semver;
-#[allow(unused_extern_crates)]
-extern crate syn;
-#[allow(unused_extern_crates)]
-extern crate tokio;
-
-/// Produces a string with an `--extern` flag for all UI test crate
-/// dependencies.
-///
-/// The dependency files are located by parsing the depinfo file for this test
-/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
-/// dependencies must be added to Cargo.toml at the project root. Test
-/// dependencies that are not *directly* used by this test module require an
-/// `extern crate` declaration.
-static EXTERN_FLAGS: LazyLock<String> = LazyLock::new(|| {
- let current_exe_depinfo = {
- let mut path = env::current_exe().unwrap();
- path.set_extension("d");
- fs::read_to_string(path).unwrap()
- };
- let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
- for line in current_exe_depinfo.lines() {
- // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
- let parse_name_path = || {
- if line.starts_with(char::is_whitespace) {
- return None;
- }
- let path_str = line.strip_suffix(':')?;
- let path = Path::new(path_str);
- if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
- return None;
- }
- let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
- // the "lib" prefix is not present for dll files
- let name = name.strip_prefix("lib").unwrap_or(name);
- Some((name, path_str))
- };
- if let Some((name, path)) = parse_name_path() {
- if TEST_DEPENDENCIES.contains(&name) {
- // A dependency may be listed twice if it is available in sysroot,
- // and the sysroot dependencies are listed first. As of the writing,
- // this only seems to apply to if_chain.
- crates.insert(name, path);
- }
- }
- }
- let not_found: Vec<&str> = TEST_DEPENDENCIES
- .iter()
- .copied()
- .filter(|n| !crates.contains_key(n))
- .collect();
- assert!(
- not_found.is_empty(),
- "dependencies not found in depinfo: {not_found:?}\n\
- help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
- help: Try adding to dev-dependencies in Cargo.toml\n\
- help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
- );
- crates
- .into_iter()
- .map(|(name, path)| format!(" --extern {name}={path}"))
- .collect()
-});
-
fn base_config(test_dir: &str) -> compiletest::Config {
let mut config = compiletest::Config {
- edition: Some("2021".into()),
- mode: TestMode::Ui,
- strict_headers: true,
- ..Default::default()
+ mode: TestMode::Yolo,
+ stderr_filters: vec![],
+ stdout_filters: vec![],
+ output_conflict_handling: if var_os("BLESS").is_some() || env::args().any(|arg| arg == "--bless") {
+ compiletest::OutputConflictHandling::Bless
+ } else {
+ compiletest::OutputConflictHandling::Error("cargo test -- -- --bless".into())
+ },
+ dependencies_crate_manifest_path: Some("clippy_test_deps/Cargo.toml".into()),
+ target: None,
+ out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap_or("target".into())).join("ui_test"),
+ ..compiletest::Config::rustc(Path::new("tests").join(test_dir))
};
- if let Ok(filters) = env::var("TESTNAME") {
- config.filters = filters.split(',').map(ToString::to_string).collect();
- }
-
- if let Some(path) = option_env!("RUSTC_LIB_PATH") {
- let path = PathBuf::from(path);
- config.run_lib_path = path.clone();
- config.compile_lib_path = path;
+ if let Some(_path) = option_env!("RUSTC_LIB_PATH") {
+ //let path = PathBuf::from(path);
+ //config.run_lib_path = path.clone();
+ //config.compile_lib_path = path;
}
let current_exe_path = env::current_exe().unwrap();
let deps_path = current_exe_path.parent().unwrap();
let profile_path = deps_path.parent().unwrap();
- // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
- // This is valuable because a) it allows us to monitor what external dependencies are used
- // and b) it ensures that conflicting rlibs are resolved properly.
- let host_libs = option_env!("HOST_LIBS")
- .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
- .unwrap_or_default();
- config.target_rustcflags = Some(format!(
- "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}",
- deps_path.display(),
- &*EXTERN_FLAGS,
- ));
-
- config.src_base = Path::new("tests").join(test_dir);
- config.build_base = profile_path.join("test").join(test_dir);
- config.rustc_path = profile_path.join(if cfg!(windows) {
+ config.program.args.push("--emit=metadata".into());
+ config.program.args.push("-Aunused".into());
+ config.program.args.push("-Zui-testing".into());
+ config.program.args.push("-Dwarnings".into());
+
+ // Normalize away slashes in windows paths.
+ config.stderr_filter(r"\\", "/");
+
+ //config.build_base = profile_path.join("test").join(test_dir);
+ config.program.program = profile_path.join(if cfg!(windows) {
"clippy-driver.exe"
} else {
"clippy-driver"
@@ -165,9 +61,18 @@ fn base_config(test_dir: &str) -> compiletest::Config {
config
}
+fn test_filter() -> Box<dyn Sync + Fn(&Path) -> bool> {
+ if let Ok(filters) = env::var("TESTNAME") {
+ let filters: Vec<_> = filters.split(',').map(ToString::to_string).collect();
+ Box::new(move |path| filters.iter().any(|f| path.to_string_lossy().contains(f)))
+ } else {
+ Box::new(|_| true)
+ }
+}
+
fn run_ui() {
- let mut config = base_config("ui");
- config.rustfix_coverage = true;
+ let config = base_config("ui");
+ //config.rustfix_coverage = true;
// use tests/clippy.toml
let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
let _threads = VarGuard::set(
@@ -179,7 +84,19 @@ fn run_ui() {
.to_string()
}),
);
- compiletest::run_tests(&config);
+ eprintln!(" Compiler: {}", config.program.display());
+
+ let name = config.root_dir.display().to_string();
+
+ let test_filter = test_filter();
+
+ compiletest::run_tests_generic(
+ config,
+ move |path| compiletest::default_file_filter(path) && test_filter(path),
+ compiletest::default_per_file_config,
+ (status_emitter::Text, status_emitter::Gha::<true> { name }),
+ )
+ .unwrap();
check_rustfix_coverage();
}
@@ -188,177 +105,118 @@ fn run_internal_tests() {
if !RUN_INTERNAL_TESTS {
return;
}
- let config = base_config("ui-internal");
- compiletest::run_tests(&config);
+ let mut config = base_config("ui-internal");
+ config.dependency_builder.args.push("--features".into());
+ config.dependency_builder.args.push("internal".into());
+ compiletest::run_tests(config).unwrap();
}
fn run_ui_toml() {
- fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
- let mut result = true;
- let opts = compiletest::test_opts(config);
- for dir in fs::read_dir(&config.src_base)? {
- let dir = dir?;
- if !dir.file_type()?.is_dir() {
- continue;
- }
- let dir_path = dir.path();
- let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
- for file in fs::read_dir(&dir_path)? {
- let file = file?;
- let file_path = file.path();
- if file.file_type()?.is_dir() {
- continue;
- }
- if file_path.extension() != Some(OsStr::new("rs")) {
- continue;
- }
- let paths = compiletest::common::TestPaths {
- file: file_path,
- base: config.src_base.clone(),
- relative_dir: dir_path.file_name().unwrap().into(),
- };
- let test_name = compiletest::make_test_name(config, &paths);
- let index = tests
- .iter()
- .position(|test| test.desc.name == test_name)
- .expect("The test should be in there");
- result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
- }
- }
- Ok(result)
- }
-
let mut config = base_config("ui-toml");
- config.src_base = config.src_base.canonicalize().unwrap();
- let tests = compiletest::make_tests(&config);
+ config.stderr_filter(
+ &regex::escape(
+ &fs::canonicalize("tests")
+ .unwrap()
+ .parent()
+ .unwrap()
+ .display()
+ .to_string()
+ .replace('\\', "/"),
+ ),
+ "$$DIR",
+ );
+
+ let name = config.root_dir.display().to_string();
+
+ let test_filter = test_filter();
- let res = run_tests(&config, tests);
- match res {
- Ok(true) => {},
- Ok(false) => panic!("Some tests failed"),
- Err(e) => {
- panic!("I/O failure during tests: {e:?}");
+ ui_test::run_tests_generic(
+ config,
+ |path| test_filter(path) && path.extension() == Some("rs".as_ref()),
+ |config, path| {
+ let mut config = config.clone();
+ config
+ .program
+ .envs
+ .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
+ Some(config)
},
- }
+ (status_emitter::Text, status_emitter::Gha::<true> { name }),
+ )
+ .unwrap();
}
fn run_ui_cargo() {
- fn run_tests(
- config: &compiletest::Config,
- filters: &[String],
- mut tests: Vec<tester::TestDescAndFn>,
- ) -> Result<bool, io::Error> {
- let mut result = true;
- let opts = compiletest::test_opts(config);
-
- for dir in fs::read_dir(&config.src_base)? {
- let dir = dir?;
- if !dir.file_type()?.is_dir() {
- continue;
- }
-
- // Use the filter if provided
- let dir_path = dir.path();
- for filter in filters {
- if !dir_path.ends_with(filter) {
- continue;
- }
- }
-
- for case in fs::read_dir(&dir_path)? {
- let case = case?;
- if !case.file_type()?.is_dir() {
- continue;
- }
-
- let src_path = case.path().join("src");
-
- // When switching between branches, if the previous branch had a test
- // that the current branch does not have, the directory is not removed
- // because an ignored Cargo.lock file exists.
- if !src_path.exists() {
- continue;
- }
-
- env::set_current_dir(&src_path)?;
-
- let cargo_toml_path = case.path().join("Cargo.toml");
- let cargo_content = fs::read(cargo_toml_path)?;
- let cargo_parsed: toml::Value = toml::from_str(
- std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
- )
- .expect("Can't parse `Cargo.toml`");
-
- let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
- let _h = VarGuard::set(
- "CARGO_PKG_RUST_VERSION",
- cargo_parsed
- .get("package")
- .and_then(|p| p.get("rust-version"))
- .and_then(toml::Value::as_str)
- .unwrap_or(""),
- );
-
- for file in fs::read_dir(&src_path)? {
- let file = file?;
- if file.file_type()?.is_dir() {
- continue;
- }
-
- // Search for the main file to avoid running a test for each file in the project
- let file_path = file.path();
- match file_path.file_name().and_then(OsStr::to_str) {
- Some("main.rs") => {},
- _ => continue,
- }
- let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
- let paths = compiletest::common::TestPaths {
- file: file_path,
- base: config.src_base.clone(),
- relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
- };
- let test_name = compiletest::make_test_name(config, &paths);
- let index = tests
- .iter()
- .position(|test| test.desc.name == test_name)
- .expect("The test should be in there");
- result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
- }
- }
- }
- Ok(result)
- }
-
if IS_RUSTC_TEST_SUITE {
return;
}
let mut config = base_config("ui-cargo");
- config.src_base = config.src_base.canonicalize().unwrap();
+ config.program.input_file_flag = CommandBuilder::cargo().input_file_flag;
+ config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag;
+ config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
+ config
+ .program
+ .envs
+ .push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
+ // We need to do this while we still have a rustc in the `program` field.
+ config.fill_host_and_target().unwrap();
+ config.dependencies_crate_manifest_path = None;
+ config.program.program.set_file_name(if cfg!(windows) {
+ "cargo-clippy.exe"
+ } else {
+ "cargo-clippy"
+ });
+ config.edition = None;
+
+ config.stderr_filter(
+ &regex::escape(
+ &fs::canonicalize("tests")
+ .unwrap()
+ .parent()
+ .unwrap()
+ .display()
+ .to_string()
+ .replace('\\', "/"),
+ ),
+ "$$DIR",
+ );
- let tests = compiletest::make_tests(&config);
+ let name = config.root_dir.display().to_string();
- let current_dir = env::current_dir().unwrap();
- let res = run_tests(&config, &config.filters, tests);
- env::set_current_dir(current_dir).unwrap();
+ let test_filter = test_filter();
- match res {
- Ok(true) => {},
- Ok(false) => panic!("Some tests failed"),
- Err(e) => {
- panic!("I/O failure during tests: {e:?}");
+ ui_test::run_tests_generic(
+ config,
+ |path| test_filter(path) && path.ends_with("Cargo.toml"),
+ |config, path| {
+ let mut config = config.clone();
+ config.out_dir = PathBuf::from("target/ui_test_cargo/").join(path.parent().unwrap());
+ Some(config)
},
- }
+ (status_emitter::Text, status_emitter::Gha::<true> { name }),
+ )
+ .unwrap();
}
-#[test]
-fn compile_test() {
+fn main() {
+ // Support being run by cargo nextest - https://nexte.st/book/custom-test-harnesses.html
+ if env::args().any(|arg| arg == "--list") {
+ if !env::args().any(|arg| arg == "--ignored") {
+ println!("compile_test: test");
+ }
+
+ return;
+ }
+
set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
run_ui();
run_ui_toml();
run_ui_cargo();
run_internal_tests();
+ rustfix_coverage_known_exceptions_accuracy();
+ ui_cargo_toml_metadata();
}
const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
@@ -384,7 +242,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
"needless_for_each_unfixable.rs",
"nonminimal_bool.rs",
"print_literal.rs",
- "print_with_newline.rs",
"redundant_static_lifetimes_multiple.rs",
"ref_binding_to_reference.rs",
"repl_uninit.rs",
@@ -399,7 +256,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
"unnecessary_lazy_eval_unfixable.rs",
"write_literal.rs",
"write_literal_2.rs",
- "write_with_newline.rs",
];
fn check_rustfix_coverage() {
@@ -432,25 +288,15 @@ fn check_rustfix_coverage() {
}
}
-#[test]
fn rustfix_coverage_known_exceptions_accuracy() {
for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS {
let rs_path = Path::new("tests/ui").join(filename);
- assert!(
- rs_path.exists(),
- "`{}` does not exist",
- rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
- );
+ assert!(rs_path.exists(), "`{}` does not exist", rs_path.display());
let fixed_path = rs_path.with_extension("fixed");
- assert!(
- !fixed_path.exists(),
- "`{}` exists",
- fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
- );
+ assert!(!fixed_path.exists(), "`{}` exists", fixed_path.display());
}
}
-#[test]
fn ui_cargo_toml_metadata() {
let ui_cargo_path = Path::new("tests/ui-cargo");
let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
diff --git a/src/tools/clippy/tests/headers.rs b/src/tools/clippy/tests/headers.rs
new file mode 100644
index 000000000..1d1e566cb
--- /dev/null
+++ b/src/tools/clippy/tests/headers.rs
@@ -0,0 +1,29 @@
+use regex::Regex;
+use std::fs;
+use walkdir::WalkDir;
+
+#[test]
+fn old_test_headers() {
+ let old_headers = Regex::new(
+ r"^//( ?\[\w+\])? ?((check|build|run|ignore|aux|only|needs|rustc|unset|no|normalize|run|compile)-|edition|incremental|revisions).*",
+ )
+ .unwrap();
+ let mut failed = false;
+
+ for entry in WalkDir::new("tests") {
+ let entry = entry.unwrap();
+ if !entry.file_type().is_file() {
+ continue;
+ }
+
+ let file = fs::read_to_string(entry.path()).unwrap();
+
+ if let Some(header) = old_headers.find(&file) {
+ println!("Found header `{}` in {}", header.as_str(), entry.path().display());
+
+ failed = true;
+ }
+ }
+
+ assert!(!failed, "use `//@foo` style test headers instead");
+}
diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs
index 8feea800f..15e5cdd69 100644
--- a/src/tools/clippy/tests/lint_message_convention.rs
+++ b/src/tools/clippy/tests/lint_message_convention.rs
@@ -20,16 +20,16 @@ impl Message {
// also no punctuation (except for "?" ?) at the end of a line
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
RegexSet::new([
- r"error: [A-Z]",
- r"help: [A-Z]",
- r"warning: [A-Z]",
- r"note: [A-Z]",
- r"try this: [A-Z]",
- r"error: .*[.!]$",
- r"help: .*[.!]$",
- r"warning: .*[.!]$",
- r"note: .*[.!]$",
- r"try this: .*[.!]$",
+ "error: [A-Z]",
+ "help: [A-Z]",
+ "warning: [A-Z]",
+ "note: [A-Z]",
+ "try this: [A-Z]",
+ "error: .*[.!]$",
+ "help: .*[.!]$",
+ "warning: .*[.!]$",
+ "note: .*[.!]$",
+ "try this: .*[.!]$",
])
.unwrap()
});
@@ -39,11 +39,11 @@ impl Message {
static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
RegexSet::new([
r"\.\.\.$",
- r".*C-like enum variant discriminant is not portable to 32-bit targets",
- r".*Intel x86 assembly syntax used",
- r".*AT&T x86 assembly syntax used",
- r"note: Clippy version: .*",
- r"the compiler unexpectedly panicked. this is a bug.",
+ ".*C-like enum variant discriminant is not portable to 32-bit targets",
+ ".*Intel x86 assembly syntax used",
+ ".*AT&T x86 assembly syntax used",
+ "note: Clippy version: .*",
+ "the compiler unexpectedly panicked. this is a bug.",
])
.unwrap()
});
diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs
index caedd5d76..0d35a22cd 100644
--- a/src/tools/clippy/tests/missing-test-files.rs
+++ b/src/tools/clippy/tests/missing-test-files.rs
@@ -41,8 +41,8 @@ fn explore_directory(dir: &Path) -> Vec<String> {
x.path().extension().and_then(OsStr::to_str),
y.path().extension().and_then(OsStr::to_str),
) {
- (Some("rs"), _) => Ordering::Less,
- (_, Some("rs")) => Ordering::Greater,
+ (Some("rs" | "toml"), _) => Ordering::Less,
+ (_, Some("rs" | "toml")) => Ordering::Greater,
_ => Ordering::Equal,
}
});
@@ -54,7 +54,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string();
if let Some(ext) = path.extension() {
match ext.to_str().unwrap() {
- "rs" => current_file = file_prefix.clone(),
+ "rs" | "toml" => current_file = file_prefix.clone(),
"stderr" | "stdout" => {
if file_prefix != current_file {
missing_files.push(path.to_str().unwrap().to_string());
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr
index 86953142b..e161507b5 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr
@@ -1,6 +1,6 @@
error: package `cargo_common_metadata_fail` is missing `package.description` metadata
- |
- = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata
@@ -12,5 +12,4 @@ error: package `cargo_common_metadata_fail` is missing `package.keywords` metada
error: package `cargo_common_metadata_fail` is missing `package.categories` metadata
-error: aborting due to 6 previous errors
-
+error: could not compile `cargo_common_metadata_fail` (bin "cargo_common_metadata_fail") due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr
index ac1b5e8e9..dbf494cc3 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr
@@ -1,6 +1,6 @@
error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata
- |
- = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata
@@ -12,5 +12,4 @@ error: package `cargo_common_metadata_fail_publish` is missing `package.keywords
error: package `cargo_common_metadata_fail_publish` is missing `package.categories` metadata
-error: aborting due to 6 previous errors
-
+error: could not compile `cargo_common_metadata_fail_publish` (bin "cargo_common_metadata_fail_publish") due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr
index be32c0dc4..ae5967406 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr
@@ -1,6 +1,6 @@
error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata
- |
- = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata
@@ -12,5 +12,4 @@ error: package `cargo_common_metadata_fail_publish_true` is missing `package.key
error: package `cargo_common_metadata_fail_publish_true` is missing `package.categories` metadata
-error: aborting due to 6 previous errors
-
+error: could not compile `cargo_common_metadata_fail_publish_true` (bin "cargo_common_metadata_fail_publish_true") due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.stderr
new file mode 100644
index 000000000..dfbf19d33
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.stderr
@@ -0,0 +1,21 @@
+warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.59.0` from `clippy.toml`
+
+error: unnecessary structure name repetition
+ --> src/main.rs:6:21
+ |
+6 | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> src/main.rs:1:9
+ |
+1 | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: unnecessary structure name repetition
+ --> src/main.rs:7:9
+ |
+7 | Foo
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: could not compile `fail-both-diff` (bin "fail-both-diff") due to 2 previous errors; 1 warning emitted
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
deleted file mode 100644
index 163f8bb35..000000000
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.59.0` from `clippy.toml`
-
-error: unnecessary structure name repetition
- --> $DIR/main.rs:6:21
- |
-LL | pub fn bar() -> Foo {
- | ^^^ help: use the applicable keyword: `Self`
- |
-note: the lint level is defined here
- --> $DIR/main.rs:1:9
- |
-LL | #![deny(clippy::use_self)]
- | ^^^^^^^^^^^^^^^^
-
-error: unnecessary structure name repetition
- --> $DIR/main.rs:7:9
- |
-LL | Foo
- | ^^^ help: use the applicable keyword: `Self`
-
-error: aborting due to 2 previous errors; 1 warning emitted
-
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.stderr
new file mode 100644
index 000000000..407a9055d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.stderr
@@ -0,0 +1,19 @@
+error: unnecessary structure name repetition
+ --> src/main.rs:6:21
+ |
+6 | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> src/main.rs:1:9
+ |
+1 | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: unnecessary structure name repetition
+ --> src/main.rs:7:9
+ |
+7 | Foo
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: could not compile `fail-both-same` (bin "fail-both-same") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
deleted file mode 100644
index 259d39b12..000000000
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: unnecessary structure name repetition
- --> $DIR/main.rs:6:21
- |
-LL | pub fn bar() -> Foo {
- | ^^^ help: use the applicable keyword: `Self`
- |
-note: the lint level is defined here
- --> $DIR/main.rs:1:9
- |
-LL | #![deny(clippy::use_self)]
- | ^^^^^^^^^^^^^^^^
-
-error: unnecessary structure name repetition
- --> $DIR/main.rs:7:9
- |
-LL | Foo
- | ^^^ help: use the applicable keyword: `Self`
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.stderr
new file mode 100644
index 000000000..566f5a686
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.stderr
@@ -0,0 +1,19 @@
+error: unnecessary structure name repetition
+ --> src/main.rs:6:21
+ |
+6 | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> src/main.rs:1:9
+ |
+1 | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: unnecessary structure name repetition
+ --> src/main.rs:7:9
+ |
+7 | Foo
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: could not compile `fail-cargo` (bin "fail-cargo") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
deleted file mode 100644
index 259d39b12..000000000
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: unnecessary structure name repetition
- --> $DIR/main.rs:6:21
- |
-LL | pub fn bar() -> Foo {
- | ^^^ help: use the applicable keyword: `Self`
- |
-note: the lint level is defined here
- --> $DIR/main.rs:1:9
- |
-LL | #![deny(clippy::use_self)]
- | ^^^^^^^^^^^^^^^^
-
-error: unnecessary structure name repetition
- --> $DIR/main.rs:7:9
- |
-LL | Foo
- | ^^^ help: use the applicable keyword: `Self`
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.stderr
new file mode 100644
index 000000000..83d4be3ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.stderr
@@ -0,0 +1,19 @@
+error: unnecessary structure name repetition
+ --> src/main.rs:6:21
+ |
+6 | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> src/main.rs:1:9
+ |
+1 | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: unnecessary structure name repetition
+ --> src/main.rs:7:9
+ |
+7 | Foo
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: could not compile `fail-clippy` (bin "fail-clippy") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
deleted file mode 100644
index 259d39b12..000000000
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: unnecessary structure name repetition
- --> $DIR/main.rs:6:21
- |
-LL | pub fn bar() -> Foo {
- | ^^^ help: use the applicable keyword: `Self`
- |
-note: the lint level is defined here
- --> $DIR/main.rs:1:9
- |
-LL | #![deny(clippy::use_self)]
- | ^^^^^^^^^^^^^^^^
-
-error: unnecessary structure name repetition
- --> $DIR/main.rs:7:9
- |
-LL | Foo
- | ^^^ help: use the applicable keyword: `Self`
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr
index 97e6c3d5a..14a6b5047 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr
@@ -1,20 +1,19 @@
error: unnecessary structure name repetition
- --> $DIR/main.rs:11:21
+ --> src/main.rs:11:21
|
-LL | pub fn bar() -> Foo {
+11 | pub fn bar() -> Foo {
| ^^^ help: use the applicable keyword: `Self`
|
note: the lint level is defined here
- --> $DIR/main.rs:6:9
+ --> src/main.rs:6:9
|
-LL | #![deny(clippy::use_self)]
+6 | #![deny(clippy::use_self)]
| ^^^^^^^^^^^^^^^^
error: unnecessary structure name repetition
- --> $DIR/main.rs:12:9
+ --> src/main.rs:12:9
|
-LL | Foo
+12 | Foo
| ^^^ help: use the applicable keyword: `Self`
-error: aborting due to 2 previous errors
-
+error: could not compile `fail-file-attr` (bin "fail-file-attr") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.stderr
index eeae5b7b2..e89388b50 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.stderr
@@ -1,4 +1,2 @@
warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.13.0` from `clippy.toml`
-warning: 1 warning emitted
-
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr
new file mode 100644
index 000000000..fde3a1e65
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr
@@ -0,0 +1,52 @@
+error: file is loaded as a module multiple times: `src/b.rs`
+ --> src/main.rs:5:1
+ |
+5 | mod b;
+ | ^^^^^^ first loaded here
+6 | / #[path = "b.rs"]
+7 | | mod b2;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+ = note: `-D clippy::duplicate-mod` implied by `-D warnings`
+
+error: file is loaded as a module multiple times: `src/c.rs`
+ --> src/main.rs:9:1
+ |
+9 | mod c;
+ | ^^^^^^ first loaded here
+10 | / #[path = "c.rs"]
+11 | | mod c2;
+ | |_______^ loaded again here
+12 | / #[path = "c.rs"]
+13 | | mod c3;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: file is loaded as a module multiple times: `src/d.rs`
+ --> src/main.rs:18:1
+ |
+18 | mod d;
+ | ^^^^^^ first loaded here
+19 | / #[path = "d.rs"]
+20 | | mod d2;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: file is loaded as a module multiple times: `src/from_other_module.rs`
+ --> src/main.rs:15:1
+ |
+15 | mod from_other_module;
+ | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
+ |
+ ::: src/other_module/mod.rs:1:1
+ |
+1 | / #[path = "../from_other_module.rs"]
+2 | | mod m;
+ | |______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: could not compile `duplicate_mod` (bin "duplicate_mod") due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
deleted file mode 100644
index 3b80d89a6..000000000
--- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
+++ /dev/null
@@ -1,53 +0,0 @@
-error: file is loaded as a module multiple times: `$DIR/b.rs`
- --> $DIR/main.rs:5:1
- |
-LL | mod b;
- | ^^^^^^ first loaded here
-LL | / #[path = "b.rs"]
-LL | | mod b2;
- | |_______^ loaded again here
- |
- = help: replace all but one `mod` item with `use` items
- = note: `-D clippy::duplicate-mod` implied by `-D warnings`
-
-error: file is loaded as a module multiple times: `$DIR/c.rs`
- --> $DIR/main.rs:9:1
- |
-LL | mod c;
- | ^^^^^^ first loaded here
-LL | / #[path = "c.rs"]
-LL | | mod c2;
- | |_______^ loaded again here
-LL | / #[path = "c.rs"]
-LL | | mod c3;
- | |_______^ loaded again here
- |
- = help: replace all but one `mod` item with `use` items
-
-error: file is loaded as a module multiple times: `$DIR/d.rs`
- --> $DIR/main.rs:18:1
- |
-LL | mod d;
- | ^^^^^^ first loaded here
-LL | / #[path = "d.rs"]
-LL | | mod d2;
- | |_______^ loaded again here
- |
- = help: replace all but one `mod` item with `use` items
-
-error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
- --> $DIR/main.rs:15:1
- |
-LL | mod from_other_module;
- | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
- |
- ::: $DIR/other_module/mod.rs:1:1
- |
-LL | / #[path = "../from_other_module.rs"]
-LL | | mod m;
- | |______^ loaded again here
- |
- = help: replace all but one `mod` item with `use` items
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr
new file mode 100644
index 000000000..da2db45d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr
@@ -0,0 +1,43 @@
+error: the "no-" prefix in the feature name "no-qaq" is negative
+ |
+ = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+ = note: `-D clippy::negative-feature-names` implied by `-D warnings`
+
+error: the "no_" prefix in the feature name "no_qaq" is negative
+ |
+ = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "not-" prefix in the feature name "not-orz" is negative
+ |
+ = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "not_" prefix in the feature name "not_orz" is negative
+ |
+ = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "-support" suffix in the feature name "qvq-support" is redundant
+ |
+ = help: consider renaming the feature to "qvq"
+ = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
+
+error: the "_support" suffix in the feature name "qvq_support" is redundant
+ |
+ = help: consider renaming the feature to "qvq"
+
+error: the "use-" prefix in the feature name "use-qwq" is redundant
+ |
+ = help: consider renaming the feature to "qwq"
+
+error: the "use_" prefix in the feature name "use_qwq" is redundant
+ |
+ = help: consider renaming the feature to "qwq"
+
+error: the "with-" prefix in the feature name "with-owo" is redundant
+ |
+ = help: consider renaming the feature to "owo"
+
+error: the "with_" prefix in the feature name "with_owo" is redundant
+ |
+ = help: consider renaming the feature to "owo"
+
+error: could not compile `feature_name` (bin "feature_name") due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
deleted file mode 100644
index c6a11fa93..000000000
--- a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
+++ /dev/null
@@ -1,44 +0,0 @@
-error: the "no-" prefix in the feature name "no-qaq" is negative
- |
- = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
- = note: `-D clippy::negative-feature-names` implied by `-D warnings`
-
-error: the "no_" prefix in the feature name "no_qaq" is negative
- |
- = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
-
-error: the "not-" prefix in the feature name "not-orz" is negative
- |
- = help: consider renaming the feature to "orz", but make sure the feature adds functionality
-
-error: the "not_" prefix in the feature name "not_orz" is negative
- |
- = help: consider renaming the feature to "orz", but make sure the feature adds functionality
-
-error: the "-support" suffix in the feature name "qvq-support" is redundant
- |
- = help: consider renaming the feature to "qvq"
- = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
-
-error: the "_support" suffix in the feature name "qvq_support" is redundant
- |
- = help: consider renaming the feature to "qvq"
-
-error: the "use-" prefix in the feature name "use-qwq" is redundant
- |
- = help: consider renaming the feature to "qwq"
-
-error: the "use_" prefix in the feature name "use_qwq" is redundant
- |
- = help: consider renaming the feature to "qwq"
-
-error: the "with-" prefix in the feature name "with-owo" is redundant
- |
- = help: consider renaming the feature to "owo"
-
-error: the "with_" prefix in the feature name "with_owo" is redundant
- |
- = help: consider renaming the feature to "owo"
-
-error: aborting due to 10 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr
new file mode 100644
index 000000000..c2907f319
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr
@@ -0,0 +1,18 @@
+error: `mod.rs` files are required, found `src/bad/inner.rs`
+ --> src/bad/inner.rs:1:1
+ |
+1 | pub mod stuff;
+ | ^
+ |
+ = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs`
+ = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+
+error: `mod.rs` files are required, found `src/bad/inner/stuff.rs`
+ --> src/bad/inner/stuff.rs:1:1
+ |
+1 | pub mod most;
+ | ^
+ |
+ = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs`
+
+error: could not compile `fail-mod` (bin "fail-mod") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
deleted file mode 100644
index 697c8b57c..000000000
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: `mod.rs` files are required, found `bad/inner.rs`
- --> $DIR/bad/inner.rs:1:1
- |
-LL | pub mod stuff;
- | ^
- |
- = help: move `bad/inner.rs` to `bad/inner/mod.rs`
- = note: `-D clippy::self-named-module-files` implied by `-D warnings`
-
-error: `mod.rs` files are required, found `bad/inner/stuff.rs`
- --> $DIR/bad/inner/stuff.rs:1:1
- |
-LL | pub mod most;
- | ^
- |
- = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs`
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
new file mode 100644
index 000000000..fcf1a3c5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
@@ -0,0 +1,10 @@
+error: `mod.rs` files are required, found `src/bad.rs`
+ --> src/bad.rs:1:1
+ |
+1 | pub mod inner;
+ | ^
+ |
+ = help: move `src/bad.rs` to `src/bad/mod.rs`
+ = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+
+error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr
deleted file mode 100644
index ea6ea9806..000000000
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: `mod.rs` files are required, found `bad.rs`
- --> /remapped/module_style/fail_mod_remap/src/bad.rs:1:1
- |
-LL | pub mod inner;
- | ^
- |
- = help: move `bad.rs` to `bad/mod.rs`
- = note: `-D clippy::self-named-module-files` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
new file mode 100644
index 000000000..f61642ca2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
@@ -0,0 +1,10 @@
+error: `mod.rs` files are not allowed, found `src/bad/mod.rs`
+ --> src/bad/mod.rs:1:1
+ |
+1 | pub struct Thing;
+ | ^
+ |
+ = help: move `src/bad/mod.rs` to `src/bad.rs`
+ = note: `-D clippy::mod-module-files` implied by `-D warnings`
+
+error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
deleted file mode 100644
index f40ceea23..000000000
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: `mod.rs` files are not allowed, found `bad/mod.rs`
- --> $DIR/bad/mod.rs:1:1
- |
-LL | pub struct Thing;
- | ^
- |
- = help: move `bad/mod.rs` to `bad.rs`
- = note: `-D clippy::mod-module-files` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.stderr
new file mode 100644
index 000000000..d82b9e73f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.stderr
@@ -0,0 +1,2 @@
+warning: using config file `$DIR/$DIR/.clippy.toml`, `$DIR/$DIR/clippy.toml` will be ignored
+
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
deleted file mode 100644
index aa1b3c638..000000000
--- a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
+++ /dev/null
@@ -1,4 +0,0 @@
-warning: using config file `$SRC_DIR/.clippy.toml`, `$SRC_DIR/clippy.toml` will be ignored
-
-warning: 1 warning emitted
-
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
index 7e96aa36f..e4de82ad3 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -10,71 +12,14 @@ dependencies = [
]
[[package]]
-name = "bitflags"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "ctrlc"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c"
-dependencies = [
- "kernel32-sys",
- "nix",
- "winapi 0.2.8",
-]
-
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.71"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
-
-[[package]]
name = "multiple_crate_versions"
version = "0.1.0"
dependencies = [
"ansi_term",
- "ctrlc",
-]
-
-[[package]]
-name = "nix"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
-dependencies = [
- "bitflags",
- "cfg-if",
- "libc",
- "void",
+ "winapi 0.2.8",
]
[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
-[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -91,12 +36,6 @@ dependencies = [
]
[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
-[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
new file mode 100644
index 000000000..5bcce9204
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
@@ -0,0 +1,5 @@
+error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
+ |
+ = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
+
+error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
index 4f97b0113..79317659a 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
@@ -6,5 +6,5 @@ publish = false
[workspace]
[dependencies]
-ctrlc = "=3.1.0"
+winapi = "0.2"
ansi_term = "=0.11.0"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
deleted file mode 100644
index f3113e093..000000000
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
- |
- = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui-cargo/update-all-references.sh b/src/tools/clippy/tests/ui-cargo/update-all-references.sh
index 4391499a1..d42043070 100755
--- a/src/tools/clippy/tests/ui-cargo/update-all-references.sh
+++ b/src/tools/clippy/tests/ui-cargo/update-all-references.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-echo "Please use 'cargo dev bless' instead."
+echo "Please use 'cargo bless' instead."
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
new file mode 100644
index 000000000..b1578c9f3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
@@ -0,0 +1,5 @@
+error: wildcard dependency for `regex`
+ |
+ = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
+
+error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr
deleted file mode 100644
index 9e65d2f99..000000000
--- a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: wildcard dependency for `regex`
- |
- = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.rs b/src/tools/clippy/tests/ui-internal/check_formulation.rs
new file mode 100644
index 000000000..43fc99603
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_formulation.rs
@@ -0,0 +1,54 @@
+#![warn(clippy::almost_standard_lint_formulation)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+declare_tool_lint! {
+ /// # What it does
+ ///
+ /// Checks for usage of correct lint formulations
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::VALID,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ /// # What it does
+ /// Check for lint formulations that are correct
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::INVALID1,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ /// # What it does
+ /// Detects uses of incorrect formulations
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::INVALID2,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ /// # What it does
+ /// Detects uses of incorrect formulations (allowed with attribute)
+ #[allow(clippy::almost_standard_lint_formulation)]
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::ALLOWED_INVALID,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Pass => [VALID, INVALID1, INVALID2]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr
new file mode 100644
index 000000000..10eabca4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr
@@ -0,0 +1,19 @@
+error: non-standard lint formulation
+ --> $DIR/check_formulation.rs:23:5
+ |
+LL | /// Check for lint formulations that are correct
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try using `Checks for` instead
+ = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings`
+
+error: non-standard lint formulation
+ --> $DIR/check_formulation.rs:33:5
+ |
+LL | /// Detects uses of incorrect formulations
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try using `Checks for` instead
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
index 99ce70283..9b0db660c 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
@@ -3,7 +3,7 @@
//@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs"
//@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
//@normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
-//@normalize-stderr-test: "running on .*" -> "running on <target>"
+//@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>"
//@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
#![deny(clippy::internal)]
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
index 0fc385cd6..b88aeae2a 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
@@ -5,10 +5,9 @@ error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
-note: rustc 1.71.0-nightly (521f4dae1 2023-05-19) running on <target>
+note: rustc <version> running on <target>
-note: compiler flags: -C prefer-dynamic -Z ui-testing
+note: compiler flags: -Z ui-testing
note: Clippy version: foo
-thread panicked while panicking. aborting.
diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.rs b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
index b0d89e038..b462b20e0 100644
--- a/src/tools/clippy/tests/ui-internal/if_chain_style.rs
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
@@ -1,5 +1,10 @@
#![warn(clippy::if_chain_style)]
-#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
+#![allow(
+ clippy::needless_if,
+ clippy::no_effect,
+ clippy::nonminimal_bool,
+ clippy::missing_clippy_version_attribute
+)]
extern crate if_chain;
diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
index d8f1ffb21..b12df2786 100644
--- a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
@@ -1,5 +1,5 @@
error: this `if` can be part of the inner `if_chain!`
- --> $DIR/if_chain_style.rs:9:5
+ --> $DIR/if_chain_style.rs:14:5
|
LL | / if true {
LL | | let x = "";
@@ -11,14 +11,14 @@ LL | | }
| |_____^
|
help: this `let` statement can also be in the `if_chain!`
- --> $DIR/if_chain_style.rs:10:9
+ --> $DIR/if_chain_style.rs:15:9
|
LL | let x = "";
| ^^^^^^^^^^^
= note: `-D clippy::if-chain-style` implied by `-D warnings`
error: `if a && b;` should be `if a; if b;`
- --> $DIR/if_chain_style.rs:19:12
+ --> $DIR/if_chain_style.rs:24:12
|
LL | if true
| ____________^
@@ -27,25 +27,25 @@ LL | | && false;
| |____________________^
error: `let` expression should be inside `then { .. }`
- --> $DIR/if_chain_style.rs:24:9
+ --> $DIR/if_chain_style.rs:29:9
|
LL | let x = "";
| ^^^^^^^^^^^
error: this `if` can be part of the outer `if_chain!`
- --> $DIR/if_chain_style.rs:35:13
+ --> $DIR/if_chain_style.rs:40:13
|
LL | if true {}
| ^^^^^^^^^^
|
help: this `let` statement can also be in the `if_chain!`
- --> $DIR/if_chain_style.rs:33:13
+ --> $DIR/if_chain_style.rs:38:13
|
LL | let x = "";
| ^^^^^^^^^^^
error: `if_chain!` only has one `if`
- --> $DIR/if_chain_style.rs:29:5
+ --> $DIR/if_chain_style.rs:34:5
|
LL | / if_chain! {
LL | | // single `if` condition
@@ -59,13 +59,13 @@ LL | | }
= note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `let` expression should be above the `if_chain!`
- --> $DIR/if_chain_style.rs:40:9
+ --> $DIR/if_chain_style.rs:45:9
|
LL | let x = "";
| ^^^^^^^^^^^
error: this `if_chain!` can be merged with the outer `if_chain!`
- --> $DIR/if_chain_style.rs:46:13
+ --> $DIR/if_chain_style.rs:51:13
|
LL | / if_chain! {
LL | | if true;
@@ -75,7 +75,7 @@ LL | | }
| |_____________^
|
help: these `let` statements can also be in the `if_chain!`
- --> $DIR/if_chain_style.rs:43:13
+ --> $DIR/if_chain_style.rs:48:13
|
LL | / let x = "";
LL | | let x = "";
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
index 23e7bc16d..c90856845 100644
--- a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::uninlined_format_args)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn main() {
let local_i32 = 1;
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
index d66b2b8ff..661350c5c 100644
--- a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::uninlined_format_args)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn main() {
let local_i32 = 1;
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
index 1be0cda12..6ec79a618 100644
--- a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
@@ -1,5 +1,5 @@
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:9:5
+ --> $DIR/uninlined_format_args.rs:10:5
|
LL | println!("val='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL + println!("val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:10:5
+ --> $DIR/uninlined_format_args.rs:11:5
|
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
|
error: literal with an empty format string
- --> $DIR/uninlined_format_args.rs:10:35
+ --> $DIR/uninlined_format_args.rs:11:35
|
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
| ^^^
@@ -37,7 +37,7 @@ LL + println!("Hello x is {:.*}", local_i32, local_f64);
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:11:5
+ --> $DIR/uninlined_format_args.rs:12:5
|
LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:12:5
+ --> $DIR/uninlined_format_args.rs:13:5
|
LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:13:5
+ --> $DIR/uninlined_format_args.rs:14:5
|
LL | println!("{}, {}", local_i32, local_opt.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs
index fb5b1b193..33f7c8ba8 100644
--- a/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs
+++ b/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs
@@ -1,4 +1,5 @@
#![warn(clippy::arithmetic_side_effects)]
+#![allow(clippy::unnecessary_literal_unwrap)]
use core::ops::{Add, Neg};
diff --git a/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr b/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr
index ad89534aa..4f98ca192 100644
--- a/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr
+++ b/src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr
@@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:68:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:69:13
|
LL | let _ = Baz + Baz;
| ^^^^^^^^^
@@ -7,49 +7,49 @@ LL | let _ = Baz + Baz;
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:79:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:80:13
|
LL | let _ = 1i32 + Baz;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:82:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:83:13
|
LL | let _ = 1i64 + Foo;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:86:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:87:13
|
LL | let _ = 1i64 + Baz;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:97:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:98:13
|
LL | let _ = Baz + 1i32;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:100:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:101:13
|
LL | let _ = Foo + 1i64;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:104:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:105:13
|
LL | let _ = Baz + 1i64;
| ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:113:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:114:13
|
LL | let _ = -Bar;
| ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects_allowed.rs:115:13
+ --> $DIR/arithmetic_side_effects_allowed.rs:116:13
|
LL | let _ = -Baz;
| ^^^^
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
index f328e4d9d..c69fcd300 100644
--- a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
+++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
@@ -1 +1,3 @@
+//@error-in-other-file: error reading Clippy's configuration file: expected `.`, `=`
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
index 28c1a568a..f7d53763a 100644
--- a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
+++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
@@ -1,4 +1,8 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4
+error: error reading Clippy's configuration file: expected `.`, `=`
+ --> $DIR/$DIR/clippy.toml:1:4
+ |
+LL | fn this_is_obviously(not: a, toml: file) {
+ | ^
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
index f328e4d9d..688c92d87 100644
--- a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
+++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
@@ -1 +1,3 @@
+//@error-in-other-file: invalid type: integer `42`, expected a sequence
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
index e3ec60192..fb0a14081 100644
--- a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
+++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
@@ -1,4 +1,8 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `disallowed-names`
+error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence
+ --> $DIR/$DIR/clippy.toml:1:20
+ |
+LL | disallowed-names = 42
+ | ^^
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
index 630bad07c..89d84eb24 100644
--- a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
@@ -1,6 +1,14 @@
-warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
+warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
+ --> $DIR/$DIR/clippy.toml:2:1
+ |
+LL | cyclomatic-complexity-threshold = 2
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead
+warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead
+ --> $DIR/$DIR/clippy.toml:3:1
+ |
+LL | blacklisted-names = [ "..", "wibble" ]
+ | ^^^^^^^^^^^^^^^^^
error: the function has a cognitive complexity of (3/2)
--> $DIR/conf_deprecated_key.rs:6:4
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml
index 63a893cc6..55789afc1 100644
--- a/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml
@@ -1,5 +1,2 @@
cognitive-complexity-threshold = 2
-# This is the deprecated name for the same key
-cyclomatic-complexity-threshold = 3
-# Check we get duplication warning regardless of order
cognitive-complexity-threshold = 4
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.rs b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.rs
index f328e4d9d..187775545 100644
--- a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.rs
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.rs
@@ -1 +1,3 @@
+//@error-in-other-file: duplicate key `cognitive-complexity-threshold`
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr
index d99490a24..7c56dfdb9 100644
--- a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr
@@ -1,8 +1,8 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`)
+error: error reading Clippy's configuration file: duplicate key `cognitive-complexity-threshold` in document root
+ --> $DIR/$DIR/clippy.toml:2:1
+ |
+LL | cognitive-complexity-threshold = 4
+ | ^
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive-complexity-threshold`
-
-warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml
new file mode 100644
index 000000000..7932c43eb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml
@@ -0,0 +1,3 @@
+cognitive-complexity-threshold = 2
+# This is the deprecated name for the same key
+cyclomatic-complexity-threshold = 3
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr
new file mode 100644
index 000000000..0af8c0add
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr
@@ -0,0 +1,14 @@
+error: error reading Clippy's configuration file: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`)
+ --> $DIR/$DIR/clippy.toml:3:1
+ |
+LL | cyclomatic-complexity-threshold = 3
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
+ --> $DIR/$DIR/clippy.toml:3:1
+ |
+LL | cyclomatic-complexity-threshold = 3
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml
new file mode 100644
index 000000000..53c634b72
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml
@@ -0,0 +1,4 @@
+# This is the deprecated name for cognitive-complexity-threshold
+cyclomatic-complexity-threshold = 3
+# Check we get duplication warning regardless of order
+cognitive-complexity-threshold = 4
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr
new file mode 100644
index 000000000..a4b1e9c33
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr
@@ -0,0 +1,14 @@
+error: error reading Clippy's configuration file: duplicate field `cognitive-complexity-threshold`
+ --> $DIR/$DIR/clippy.toml:4:1
+ |
+LL | cognitive-complexity-threshold = 4
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
+ --> $DIR/$DIR/clippy.toml:2:1
+ |
+LL | cyclomatic-complexity-threshold = 3
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs
new file mode 100644
index 000000000..ebadd4e44
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs
@@ -0,0 +1,472 @@
+// NOTE: Copied from `ui/auxiliary/proc_macros.rs`, couldn't get `../` to work for some reason
+
+#![feature(let_chains)]
+#![feature(proc_macro_span)]
+#![allow(clippy::excessive_nesting, dead_code)]
+
+extern crate proc_macro;
+
+use core::mem;
+use proc_macro::{
+ token_stream::IntoIter,
+ Delimiter::{self, Brace, Parenthesis},
+ Group, Ident, Literal, Punct,
+ Spacing::{self, Alone, Joint},
+ Span, TokenStream, TokenTree as TT,
+};
+
+type Result<T> = core::result::Result<T, TokenStream>;
+
+/// Make a `compile_error!` pointing to the given span.
+fn make_error(msg: &str, span: Span) -> TokenStream {
+ TokenStream::from_iter([
+ TT::Ident(Ident::new("compile_error", span)),
+ TT::Punct(punct_with_span('!', Alone, span)),
+ TT::Group({
+ let mut msg = Literal::string(msg);
+ msg.set_span(span);
+ group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span)
+ }),
+ ])
+}
+
+fn expect_tt<T>(tt: Option<TT>, f: impl FnOnce(TT) -> Option<T>, expected: &str, span: Span) -> Result<T> {
+ match tt {
+ None => Err(make_error(
+ &format!("unexpected end of input, expected {expected}"),
+ span,
+ )),
+ Some(tt) => {
+ let span = tt.span();
+ match f(tt) {
+ Some(x) => Ok(x),
+ None => Err(make_error(&format!("unexpected token, expected {expected}"), span)),
+ }
+ },
+ }
+}
+
+fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct {
+ let mut p = Punct::new(c, spacing);
+ p.set_span(span);
+ p
+}
+
+fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group {
+ let mut g = Group::new(delimiter, stream);
+ g.set_span(span);
+ g
+}
+
+/// Token used to escape the following token from the macro's span rules.
+const ESCAPE_CHAR: char = '$';
+
+/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their
+/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
+#[proc_macro]
+pub fn with_span(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let span = iter.next().unwrap().span();
+ let mut res = TokenStream::new();
+ if let Err(e) = write_with_span(span, iter, &mut res) {
+ e
+ } else {
+ res
+ }
+}
+
+/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be
+/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`.
+#[proc_macro]
+pub fn external(input: TokenStream) -> TokenStream {
+ let mut res = TokenStream::new();
+ if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) {
+ e
+ } else {
+ res
+ }
+}
+
+/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped
+/// either by `#ident` or `#(tokens)`.
+fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> {
+ while let Some(tt) = input.next() {
+ match tt {
+ TT::Punct(p) if p.as_char() == ESCAPE_CHAR => {
+ expect_tt(
+ input.next(),
+ |tt| match tt {
+ tt @ (TT::Ident(_) | TT::Literal(_)) => {
+ out.extend([tt]);
+ Some(())
+ },
+ TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => {
+ p.set_span(s);
+ out.extend([TT::Punct(p)]);
+ Some(())
+ },
+ TT::Group(g) if g.delimiter() == Parenthesis => {
+ out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]);
+ Some(())
+ },
+ _ => None,
+ },
+ "an ident, a literal, or parenthesized tokens",
+ p.span(),
+ )?;
+ },
+ TT::Group(g) => {
+ let mut stream = TokenStream::new();
+ write_with_span(s, g.stream().into_iter(), &mut stream)?;
+ out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]);
+ },
+ mut tt => {
+ tt.set_span(s);
+ out.extend([tt]);
+ },
+ }
+ }
+ Ok(())
+}
+
+/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
+/// contained tokens as though they came from a macro expansion.
+///
+/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument
+/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or
+/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will
+/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another
+/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will
+/// expand the remaining tokens as a single argument.
+///
+/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro
+/// calls. However, any arguments will be passed as though they came from the outermost context.
+#[proc_macro_attribute]
+pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream {
+ let mut args = args.into_iter();
+ let mac_name = match args.next() {
+ Some(TT::Ident(name)) => Some(name),
+ Some(tt) => {
+ return make_error(
+ "unexpected argument, expected either an ident or no arguments",
+ tt.span(),
+ );
+ },
+ None => None,
+ };
+ if let Some(tt) = args.next() {
+ return make_error(
+ "unexpected argument, expected either an ident or no arguments",
+ tt.span(),
+ );
+ };
+
+ let mac_name = if let Some(mac_name) = mac_name {
+ Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site())
+ } else {
+ let mut input = match LookaheadIter::new(input.clone().into_iter()) {
+ Some(x) => x,
+ None => return input,
+ };
+ loop {
+ match input.next() {
+ None => break Ident::new("__inline_mac", Span::call_site()),
+ Some(TT::Ident(kind)) => match &*kind.to_string() {
+ "impl" => break Ident::new("__inline_mac_impl", Span::call_site()),
+ kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => {
+ if let TT::Ident(name) = &input.tt {
+ break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site());
+ } else {
+ break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site());
+ }
+ },
+ _ => {},
+ },
+ _ => {},
+ }
+ }
+ };
+
+ let mut expander = Expander::default();
+ let mut mac = MacWriter::new(mac_name);
+ if let Err(e) = expander.expand(input.into_iter(), &mut mac) {
+ return e;
+ }
+ let mut out = TokenStream::new();
+ mac.finish(&mut out);
+ out.extend(expander.expn);
+ out
+}
+
+/// Wraps a `TokenStream` iterator with a single token lookahead.
+struct LookaheadIter {
+ tt: TT,
+ iter: IntoIter,
+}
+impl LookaheadIter {
+ fn new(mut iter: IntoIter) -> Option<Self> {
+ iter.next().map(|tt| Self { tt, iter })
+ }
+
+ /// Get's the lookahead token, replacing it with the next token in the stream.
+ /// Note: If there isn't a next token, this will not return the lookahead token.
+ fn next(&mut self) -> Option<TT> {
+ self.iter.next().map(|tt| mem::replace(&mut self.tt, tt))
+ }
+}
+
+/// Builds the macro used to implement all the `inline!` macro calls.
+struct MacWriter {
+ name: Ident,
+ macros: TokenStream,
+ next_idx: usize,
+}
+impl MacWriter {
+ fn new(name: Ident) -> Self {
+ Self {
+ name,
+ macros: TokenStream::new(),
+ next_idx: 0,
+ }
+ }
+
+ /// Inserts a new `inline!` call.
+ fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> {
+ let idx = self.next_idx;
+ self.next_idx += 1;
+
+ let mut inner = Expander::for_arm(idx);
+ inner.expand(body.stream().into_iter(), self)?;
+ let new_arm = inner.arm.unwrap();
+
+ self.macros.extend([
+ TT::Group(Group::new(Parenthesis, new_arm.args_def)),
+ TT::Punct(Punct::new('=', Joint)),
+ TT::Punct(Punct::new('>', Alone)),
+ TT::Group(Group::new(Parenthesis, inner.expn)),
+ TT::Punct(Punct::new(';', Alone)),
+ ]);
+
+ expander.expn.extend([
+ TT::Ident({
+ let mut name = self.name.clone();
+ name.set_span(name_span);
+ name
+ }),
+ TT::Punct(punct_with_span('!', Alone, bang_span)),
+ ]);
+ let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]);
+ if let Some(arm) = expander.arm.as_mut() {
+ if !new_arm.args.is_empty() {
+ arm.add_sub_args(new_arm.args, &mut call_body);
+ }
+ } else {
+ call_body.extend(new_arm.args);
+ }
+ let mut g = Group::new(body.delimiter(), call_body);
+ g.set_span(body.span());
+ expander.expn.extend([TT::Group(g)]);
+ Ok(())
+ }
+
+ /// Creates the macro definition.
+ fn finish(self, out: &mut TokenStream) {
+ if self.next_idx != 0 {
+ out.extend([
+ TT::Ident(Ident::new("macro_rules", Span::call_site())),
+ TT::Punct(Punct::new('!', Alone)),
+ TT::Ident(self.name),
+ TT::Group(Group::new(Brace, self.macros)),
+ ])
+ }
+ }
+}
+
+struct MacroArm {
+ args_def: TokenStream,
+ args: Vec<TT>,
+}
+impl MacroArm {
+ fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(Ident::new(kind, Span::call_site())),
+ ]);
+ name.set_span(arg_span);
+ out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
+ }
+
+ fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(kind),
+ ]),
+ ))]);
+ name.set_span(arg_span);
+ out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
+ }
+
+ fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(Ident::new("tt", Span::call_site())),
+ ]),
+ )),
+ TT::Punct(Punct::new('*', Alone)),
+ ]),
+ ))]);
+ name.set_span(arg_span);
+ out.extend([
+ TT::Punct(punct_with_span('$', Alone, dollar_span)),
+ TT::Group(group_with_span(
+ Parenthesis,
+ TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]),
+ dollar_span,
+ )),
+ TT::Punct(punct_with_span('*', Alone, dollar_span)),
+ ]);
+ }
+
+ fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> {
+ match tt {
+ TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]),
+ TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => {
+ let lt_name = expect_tt(
+ input.next(),
+ |tt| match tt {
+ TT::Ident(x) => Some(x),
+ _ => None,
+ },
+ "lifetime name",
+ p.span(),
+ )?;
+ let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span());
+ self.add_single_arg_def("lifetime", dollar_span, arg_span, out);
+ self.args.extend([TT::Punct(p), TT::Ident(lt_name)]);
+ },
+ TT::Ident(x) => {
+ self.add_single_arg_def("ident", dollar_span, x.span(), out);
+ self.args.push(TT::Ident(x));
+ },
+ TT::Literal(x) => {
+ self.add_single_arg_def("literal", dollar_span, x.span(), out);
+ self.args.push(TT::Literal(x));
+ },
+ TT::Group(g) if g.delimiter() == Parenthesis => {
+ let mut inner = g.stream().into_iter();
+ if let Some(TT::Punct(p)) = inner.next()
+ && p.as_char() == '@'
+ {
+ let kind = expect_tt(
+ inner.next(),
+ |tt| match tt {
+ TT::Ident(kind) => Some(kind),
+ _ => None,
+ },
+ "a macro fragment specifier",
+ 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())))
+ } else {
+ self.add_multi_arg_def(dollar_span, g.span(), out);
+ self.args.push(TT::Group(g));
+ }
+ },
+ tt => return Err(make_error("unsupported escape", tt.span())),
+ };
+ Ok(())
+ }
+
+ fn add_sub_args(&mut self, args: Vec<TT>, out: &mut TokenStream) {
+ self.add_multi_arg_def(Span::call_site(), Span::call_site(), out);
+ self.args
+ .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]);
+ }
+}
+
+#[derive(Default)]
+struct Expander {
+ arm: Option<MacroArm>,
+ expn: TokenStream,
+}
+impl Expander {
+ fn for_arm(idx: usize) -> Self {
+ Self {
+ arm: Some(MacroArm {
+ args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]),
+ args: Vec::new(),
+ }),
+ expn: TokenStream::new(),
+ }
+ }
+
+ fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> {
+ match tt {
+ TT::Group(g) => {
+ let outer = mem::take(&mut self.expn);
+ self.expand(g.stream().into_iter(), mac)?;
+ let inner = mem::replace(&mut self.expn, outer);
+ self.expn
+ .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]);
+ },
+ tt => self.expn.extend([tt]),
+ }
+ Ok(())
+ }
+
+ fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> {
+ let Some(mut input) = LookaheadIter::new(input) else {
+ return Ok(());
+ };
+ while let Some(tt) = input.next() {
+ if let TT::Punct(p) = &tt
+ && 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)?;
+ if input.next().is_none() {
+ return Ok(());
+ }
+ } else if let TT::Punct(p) = &input.tt
+ && p.as_char() == '!'
+ && let TT::Ident(name) = &tt
+ && name.to_string() == "inline"
+ {
+ let g = expect_tt(
+ input.iter.next(),
+ |tt| match tt {
+ TT::Group(g) => Some(g),
+ _ => None,
+ },
+ "macro arguments",
+ p.span(),
+ )?;
+ mac.insert(name.span(), p.span(), g, self)?;
+ if input.next().is_none() {
+ return Ok(());
+ }
+ } else {
+ self.write_tt(tt, mac)?;
+ }
+ }
+ self.write_tt(input.tt, mac)
+ }
+}
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/clippy.toml b/src/tools/clippy/tests/ui-toml/excessive_nesting/clippy.toml
new file mode 100644
index 000000000..e60ac978c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/clippy.toml
@@ -0,0 +1 @@
+excessive-nesting-threshold = 4
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
new file mode 100644
index 000000000..c28220b97
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
@@ -0,0 +1,197 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![rustfmt::skip]
+#![feature(custom_inner_attributes)]
+#![allow(unused)]
+#![allow(clippy::let_and_return)]
+#![allow(clippy::redundant_closure_call)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::needless_if)]
+#![warn(clippy::excessive_nesting)]
+#![allow(clippy::collapsible_if)]
+
+#[macro_use]
+extern crate proc_macros;
+
+static X: u32 = {
+ let x = {
+ let y = {
+ let z = {
+ let w = { 3 };
+ w
+ };
+ z
+ };
+ y
+ };
+ x
+};
+
+macro_rules! xx {
+ () => {{
+ {
+ {
+ {
+ {
+ {
+ {
+ {
+ {
+ {
+ {
+ println!("ehe"); // should not lint
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }};
+}
+
+struct A;
+
+impl A {
+ pub fn a(&self, v: u32) {
+ struct B;
+
+ impl B {
+ pub fn b() {
+ struct C;
+
+ impl C {
+ pub fn c() {}
+ }
+ }
+ }
+ }
+}
+
+struct D { d: u32 }
+
+trait Lol {
+ fn lmao() {
+ fn bb() {
+ fn cc() {
+ let x = { 1 }; // not a warning, but cc is
+ }
+
+ let x = { 1 }; // warning
+ }
+ }
+}
+
+#[allow(clippy::excessive_nesting)]
+fn l() {{{{{{{{{}}}}}}}}}
+
+use a::{b::{c::{d::{e::{f::{}}}}}}; // should not lint
+
+pub mod a {
+ pub mod b {
+ pub mod c {
+ pub mod d {
+ pub mod e {
+ pub mod f {}
+ } // not here
+ } // only warning should be here
+ }
+ }
+}
+
+fn a_but_not(v: u32) {}
+
+fn main() {
+ let a = A;
+
+ a_but_not({{{{{{{{0}}}}}}}});
+ a.a({{{{{{{{{0}}}}}}}}});
+ (0, {{{{{{{1}}}}}}});
+
+ if true {
+ if true {
+ if true {
+ if true {
+ if true {
+
+ }
+ }
+ }
+ }
+ }
+
+ let y = (|| {
+ let x = (|| {
+ let y = (|| {
+ let z = (|| {
+ let w = { 3 };
+ w
+ })();
+ z
+ })();
+ y
+ })();
+ x
+ })();
+
+ external! { {{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}} }; // ensure this isn't linted in external macros
+ with_span! { span {{{{{{{{{{{{}}}}}}}}}}}} }; // don't lint for proc macros
+ xx!(); // ensure this is never linted
+ let boo = true;
+ !{boo as u32 + !{boo as u32 + !{boo as u32}}};
+
+ // this is a mess, but that's intentional
+ let mut y = 1;
+ y += {{{{{5}}}}};
+ let z = y + {{{{{{{{{5}}}}}}}}};
+ [0, {{{{{{{{{{0}}}}}}}}}}];
+ let mut xx = [0; {{{{{{{{100}}}}}}}}];
+ xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}];
+ &mut {{{{{{{{{{y}}}}}}}}}};
+
+ for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
+
+ while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
+
+ while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
+
+ let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} };
+
+ {{{{1;}}}}..{{{{{{3}}}}}};
+ {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
+ ..{{{{{{{5}}}}}}};
+ ..={{{{{3}}}}};
+ {{{{{1;}}}}}..;
+
+ loop { break {{{{1}}}} };
+ loop {{{{{{}}}}}}
+
+ match {{{{{{true}}}}}} {
+ true => {{{{}}}},
+ false => {{{{}}}},
+ }
+
+ {
+ {
+ {
+ {
+ println!("warning! :)");
+ }
+ }
+ }
+ }
+}
+
+async fn b() -> u32 {
+ async fn c() -> u32 {{{{{{{0}}}}}}}
+
+ c().await
+}
+
+async fn a() {
+ {{{{b().await}}}};
+}
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr
new file mode 100644
index 000000000..1a7311b33
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr
@@ -0,0 +1,314 @@
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:21:25
+ |
+LL | let w = { 3 };
+ | ^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+ = note: `-D clippy::excessive-nesting` implied by `-D warnings`
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:67:17
+ |
+LL | / impl C {
+LL | | pub fn c() {}
+LL | | }
+ | |_________________^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:81:25
+ |
+LL | let x = { 1 }; // not a warning, but cc is
+ | ^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:98:17
+ |
+LL | / pub mod e {
+LL | | pub mod f {}
+LL | | } // not here
+ | |_________________^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:111:18
+ |
+LL | a_but_not({{{{{{{{0}}}}}}}});
+ | ^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:112:12
+ |
+LL | a.a({{{{{{{{{0}}}}}}}}});
+ | ^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:113:12
+ |
+LL | (0, {{{{{{{1}}}}}}});
+ | ^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:118:25
+ |
+LL | if true {
+ | _________________________^
+LL | | if true {
+LL | |
+LL | | }
+LL | | }
+ | |_________________^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:130:29
+ |
+LL | let z = (|| {
+ | _____________________________^
+LL | | let w = { 3 };
+LL | | w
+LL | | })();
+ | |_________________^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:149:13
+ |
+LL | y += {{{{{5}}}}};
+ | ^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:150:20
+ |
+LL | let z = y + {{{{{{{{{5}}}}}}}}};
+ | ^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:151:12
+ |
+LL | [0, {{{{{{{{{{0}}}}}}}}}}];
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:152:25
+ |
+LL | let mut xx = [0; {{{{{{{{100}}}}}}}}];
+ | ^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:153:11
+ |
+LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:154:13
+ |
+LL | &mut {{{{{{{{{{y}}}}}}}}}};
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:156:17
+ |
+LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
+ | ^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:156:28
+ |
+LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
+ | ^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:158:28
+ |
+LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
+ | ^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:158:48
+ |
+LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
+ | ^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:160:14
+ |
+LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:160:35
+ |
+LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
+ | ^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:162:23
+ |
+LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:164:8
+ |
+LL | {{{{1;}}}}..{{{{{{3}}}}}};
+ | ^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:164:20
+ |
+LL | {{{{1;}}}}..{{{{{{3}}}}}};
+ | ^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:165:8
+ |
+LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
+ | ^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:165:21
+ |
+LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:166:10
+ |
+LL | ..{{{{{{{5}}}}}}};
+ | ^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:167:11
+ |
+LL | ..={{{{{3}}}}};
+ | ^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:168:8
+ |
+LL | {{{{{1;}}}}}..;
+ | ^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:170:20
+ |
+LL | loop { break {{{{1}}}} };
+ | ^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:171:13
+ |
+LL | loop {{{{{{}}}}}}
+ | ^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:173:14
+ |
+LL | match {{{{{{true}}}}}} {
+ | ^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:174:20
+ |
+LL | true => {{{{}}}},
+ | ^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:175:21
+ |
+LL | false => {{{{}}}},
+ | ^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:181:17
+ |
+LL | / {
+LL | | println!("warning! :)");
+LL | | }
+ | |_________________^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:190:28
+ |
+LL | async fn c() -> u32 {{{{{{{0}}}}}}}
+ | ^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: this block is too nested
+ --> $DIR/excessive_nesting.rs:196:8
+ |
+LL | {{{{b().await}}}};
+ | ^^^^^^^^^^^
+ |
+ = help: try refactoring your code to minimize nesting
+
+error: aborting due to 37 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
index 9e267c893..206788e19 100644
--- a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
+++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
@@ -1,5 +1,6 @@
//@compile-flags: --test
#![warn(clippy::expect_used)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn expect_option() {
let opt = Some(0);
diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
index 1e9bb48c3..9eef0e1bf 100644
--- a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
+++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
@@ -1,5 +1,5 @@
error: used `expect()` on an `Option` value
- --> $DIR/expect_used.rs:6:13
+ --> $DIR/expect_used.rs:7:13
|
LL | let _ = opt.expect("");
| ^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let _ = opt.expect("");
= note: `-D clippy::expect-used` implied by `-D warnings`
error: used `expect()` on a `Result` value
- --> $DIR/expect_used.rs:11:13
+ --> $DIR/expect_used.rs:12:13
|
LL | let _ = res.expect("");
| ^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
index d623ac7e0..4882416c4 100644
--- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
+++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
@@ -1,5 +1,5 @@
#![warn(clippy::ifs_same_cond)]
-#![allow(clippy::if_same_then_else, clippy::comparison_chain)]
+#![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)]
fn main() {}
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 2ebf28645..03fa71997 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,3 +1,5 @@
+//@error-in-other-file: `invalid.version` is not a valid Rust version
+
#![allow(clippy::redundant_clone)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs
index 2498672d7..bd5110138 100644
--- a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs
+++ b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs
@@ -1,5 +1,5 @@
#![allow(clippy::excessive_precision)]
-#[deny(clippy::unreadable_literal)]
+#![warn(clippy::unreadable_literal)]
fn allow_inconsistent_digit_grouping() {
#![allow(clippy::inconsistent_digit_grouping)]
diff --git a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr
index be505bda4..ac9d89d0c 100644
--- a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr
@@ -6,5 +6,13 @@ LL | let _fail1 = 100_200_300.123456789;
|
= note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
-error: aborting due to previous error
+error: long literal lacking separators
+ --> $DIR/test.rs:22:18
+ |
+LL | let _fail2 = 100200300.300200100;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.300_200_100`
+ |
+ = note: `-D clippy::unreadable-literal` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
index 21849a14f..da76bb20f 100644
--- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
@@ -3,6 +3,7 @@
fn below_limit() {
let slice: Option<&[u32]> = Some(&[1, 2, 3]);
if let Some(slice) = slice {
+ //~^ ERROR: binding can be a slice pattern
// This would usually not be linted but is included now due to the
// index limit in the config file
println!("{}", slice[7]);
diff --git a/src/tools/clippy/tests/ui-toml/min_ident_chars/auxiliary/extern_types.rs b/src/tools/clippy/tests/ui-toml/min_ident_chars/auxiliary/extern_types.rs
new file mode 100644
index 000000000..06a144f22
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_ident_chars/auxiliary/extern_types.rs
@@ -0,0 +1,3 @@
+#![allow(nonstandard_style, unused)]
+
+pub struct Aaa;
diff --git a/src/tools/clippy/tests/ui-toml/min_ident_chars/clippy.toml b/src/tools/clippy/tests/ui-toml/min_ident_chars/clippy.toml
new file mode 100644
index 000000000..0114ca750
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_ident_chars/clippy.toml
@@ -0,0 +1,2 @@
+allowed-idents-below-min-chars = ["Owo", "Uwu", "wha", "t_e", "lse", "_do", "_i_", "put", "her", "_e"]
+min-ident-chars-threshold = 3
diff --git a/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.rs b/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.rs
new file mode 100644
index 000000000..4326c7159
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.rs
@@ -0,0 +1,19 @@
+//@aux-build:extern_types.rs
+#![allow(nonstandard_style, unused)]
+#![warn(clippy::min_ident_chars)]
+
+extern crate extern_types;
+use extern_types::Aaa;
+
+struct Owo {
+ Uwu: u128,
+ aaa: Aaa,
+}
+
+fn main() {
+ let wha = 1;
+ let vvv = 1;
+ let uuu = 1;
+ let (mut a, mut b) = (1, 2);
+ for i in 0..1000 {}
+}
diff --git a/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.stderr b/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.stderr
new file mode 100644
index 000000000..d9a27628d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_ident_chars/min_ident_chars.stderr
@@ -0,0 +1,46 @@
+error: this ident is too short (3 <= 3)
+ --> $DIR/min_ident_chars.rs:6:19
+ |
+LL | use extern_types::Aaa;
+ | ^^^
+ |
+ = note: `-D clippy::min-ident-chars` implied by `-D warnings`
+
+error: this ident is too short (3 <= 3)
+ --> $DIR/min_ident_chars.rs:10:5
+ |
+LL | aaa: Aaa,
+ | ^^^
+
+error: this ident is too short (3 <= 3)
+ --> $DIR/min_ident_chars.rs:15:9
+ |
+LL | let vvv = 1;
+ | ^^^
+
+error: this ident is too short (3 <= 3)
+ --> $DIR/min_ident_chars.rs:16:9
+ |
+LL | let uuu = 1;
+ | ^^^
+
+error: this ident is too short (1 <= 3)
+ --> $DIR/min_ident_chars.rs:17:14
+ |
+LL | let (mut a, mut b) = (1, 2);
+ | ^
+
+error: this ident is too short (1 <= 3)
+ --> $DIR/min_ident_chars.rs:17:21
+ |
+LL | let (mut a, mut b) = (1, 2);
+ | ^
+
+error: this ident is too short (1 <= 3)
+ --> $DIR/min_ident_chars.rs:18:9
+ |
+LL | for i in 0..1000 {}
+ | ^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
index 1e3ec123a..e1dc3f438 100644
--- a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
@@ -41,7 +41,7 @@ fn match_like_matches() {
fn match_same_arms() {
match (1, 2, 3) {
(1, .., 3) => 42,
- (.., 3) => 42, //~ ERROR match arms have same body
+ (.., 3) => 42,
_ => 0,
};
}
@@ -49,7 +49,7 @@ fn match_same_arms() {
fn match_same_arms2() {
let _ = match Some(42) {
Some(_) => 24,
- None => 24, //~ ERROR match arms have same body
+ None => 24,
};
}
diff --git a/src/tools/clippy/tests/ui-toml/module_inception/clippy.toml b/src/tools/clippy/tests/ui-toml/module_inception/clippy.toml
new file mode 100644
index 000000000..787620d86
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/module_inception/clippy.toml
@@ -0,0 +1 @@
+allow-private-module-inception = true
diff --git a/src/tools/clippy/tests/ui-toml/module_inception/module_inception.rs b/src/tools/clippy/tests/ui-toml/module_inception/module_inception.rs
new file mode 100644
index 000000000..cd495c884
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/module_inception/module_inception.rs
@@ -0,0 +1,34 @@
+#![warn(clippy::module_inception)]
+
+// Lint
+pub mod foo2 {
+ pub mod bar2 {
+ pub mod bar2 {
+ pub mod foo2 {}
+ }
+ pub mod foo2 {}
+ }
+ pub mod foo2 {
+ pub mod bar2 {}
+ }
+}
+
+// Don't lint
+mod foo {
+ pub mod bar {
+ pub mod foo {
+ pub mod bar {}
+ }
+ }
+ pub mod foo {
+ pub mod bar {}
+ }
+}
+
+// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>.
+pub mod bar {
+ #[allow(clippy::module_inception)]
+ pub mod bar {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/module_inception/module_inception.stderr b/src/tools/clippy/tests/ui-toml/module_inception/module_inception.stderr
new file mode 100644
index 000000000..a5a09c322
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/module_inception/module_inception.stderr
@@ -0,0 +1,20 @@
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:6:9
+ |
+LL | / pub mod bar2 {
+LL | | pub mod foo2 {}
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::module-inception` implied by `-D warnings`
+
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:11:5
+ |
+LL | / pub mod foo2 {
+LL | | pub mod bar2 {}
+LL | | }
+ | |_____^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
index f5761c6af..e7ac05dd3 100644
--- a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
@@ -1,8 +1,3 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
extern crate proc_macro;
use proc_macro::TokenStream;
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed
index e4747bedd..054db5d93 100644
--- a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
//@run-rustfix
#![warn(clippy::nonstandard_macro_braces)]
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
index 54edded99..95d1a2297 100644
--- a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
//@run-rustfix
#![warn(clippy::nonstandard_macro_braces)]
diff --git a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.rs b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.rs
index 5a2df9f6c..17c1b03d8 100644
--- a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.rs
+++ b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.rs
@@ -3,11 +3,17 @@
// We also check the out_of_bounds_indexing lint here, because it lints similar things and
// we want to avoid false positives.
#![warn(clippy::out_of_bounds_indexing)]
-#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(
+ unconditional_panic,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::useless_vec
+)]
const ARR: [i32; 2] = [1, 2];
const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+//~^ ERROR: failed
const fn idx() -> usize {
1
@@ -29,6 +35,8 @@ fn main() {
x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+ //
+ //~^^ ERROR: failed
let y = &x;
y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
diff --git a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
index bc178b7e1..14e131944 100644
--- a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
@@ -1,17 +1,17 @@
error[E0080]: evaluation of `main::{constant#3}` failed
- --> $DIR/test.rs:31:14
+ --> $DIR/test.rs:37:14
|
LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
note: erroneous constant used
- --> $DIR/test.rs:31:5
+ --> $DIR/test.rs:37:5
|
LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
| ^^^^^^^^^^^^^^^^^^^^^^
error: indexing may panic
- --> $DIR/test.rs:22:5
+ --> $DIR/test.rs:28:5
|
LL | x[index];
| ^^^^^^^^
@@ -20,7 +20,7 @@ LL | x[index];
= note: `-D clippy::indexing-slicing` implied by `-D warnings`
error: indexing may panic
- --> $DIR/test.rs:38:5
+ --> $DIR/test.rs:46:5
|
LL | v[0];
| ^^^^
@@ -28,7 +28,7 @@ LL | v[0];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/test.rs:39:5
+ --> $DIR/test.rs:47:5
|
LL | v[10];
| ^^^^^
@@ -36,7 +36,7 @@ LL | v[10];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/test.rs:40:5
+ --> $DIR/test.rs:48:5
|
LL | v[1 << 3];
| ^^^^^^^^^
@@ -44,7 +44,7 @@ LL | v[1 << 3];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/test.rs:46:5
+ --> $DIR/test.rs:54:5
|
LL | v[N];
| ^^^^
@@ -52,7 +52,7 @@ LL | v[N];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/test.rs:47:5
+ --> $DIR/test.rs:55:5
|
LL | v[M];
| ^^^^
@@ -60,7 +60,7 @@ LL | v[M];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error[E0080]: evaluation of constant value failed
- --> $DIR/test.rs:10:24
+ --> $DIR/test.rs:15:24
|
LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
index 8e1a1710a..63fdea710 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
@@ -1,6 +1,8 @@
//@compile-flags: --crate-name conf_disallowed_methods
+#![allow(clippy::needless_raw_strings)]
#![warn(clippy::disallowed_methods)]
+#![allow(clippy::useless_vec)]
extern crate futures;
extern crate regex;
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
index 148d1cae5..fc137c225 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
@@ -1,5 +1,5 @@
error: use of a disallowed method `regex::Regex::new`
- --> $DIR/conf_disallowed_methods.rs:33:14
+ --> $DIR/conf_disallowed_methods.rs:35:14
|
LL | let re = Regex::new(r"ab.*c").unwrap();
| ^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap();
= note: `-D clippy::disallowed-methods` implied by `-D warnings`
error: use of a disallowed method `regex::Regex::is_match`
- --> $DIR/conf_disallowed_methods.rs:34:5
+ --> $DIR/conf_disallowed_methods.rs:36:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
@@ -15,73 +15,73 @@ LL | re.is_match("abc");
= note: no matching allowed (from clippy.toml)
error: use of a disallowed method `std::iter::Iterator::sum`
- --> $DIR/conf_disallowed_methods.rs:37:5
+ --> $DIR/conf_disallowed_methods.rs:39:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `slice::sort_unstable`
- --> $DIR/conf_disallowed_methods.rs:39:5
+ --> $DIR/conf_disallowed_methods.rs:41:5
|
LL | a.sort_unstable();
| ^^^^^^^^^^^^^^^^^
error: use of a disallowed method `f32::clamp`
- --> $DIR/conf_disallowed_methods.rs:41:13
+ --> $DIR/conf_disallowed_methods.rs:43:13
|
LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `regex::Regex::new`
- --> $DIR/conf_disallowed_methods.rs:44:61
+ --> $DIR/conf_disallowed_methods.rs:46:61
|
LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
| ^^^^^^^^^^
error: use of a disallowed method `f32::clamp`
- --> $DIR/conf_disallowed_methods.rs:47:28
+ --> $DIR/conf_disallowed_methods.rs:49:28
|
LL | let in_call = Box::new(f32::clamp);
| ^^^^^^^^^^
error: use of a disallowed method `regex::Regex::new`
- --> $DIR/conf_disallowed_methods.rs:48:53
+ --> $DIR/conf_disallowed_methods.rs:50:53
|
LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new);
| ^^^^^^^^^^
error: use of a disallowed method `futures::stream::select_all`
- --> $DIR/conf_disallowed_methods.rs:51:31
+ --> $DIR/conf_disallowed_methods.rs:53:31
|
LL | let same_name_as_module = select_all(vec![empty::<()>()]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::local_fn`
- --> $DIR/conf_disallowed_methods.rs:53:5
+ --> $DIR/conf_disallowed_methods.rs:55:5
|
LL | local_fn();
| ^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::local_mod::f`
- --> $DIR/conf_disallowed_methods.rs:54:5
+ --> $DIR/conf_disallowed_methods.rs:56:5
|
LL | local_mod::f();
| ^^^^^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Struct::method`
- --> $DIR/conf_disallowed_methods.rs:56:5
+ --> $DIR/conf_disallowed_methods.rs:58:5
|
LL | s.method();
| ^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method`
- --> $DIR/conf_disallowed_methods.rs:57:5
+ --> $DIR/conf_disallowed_methods.rs:59:5
|
LL | s.provided_method();
| ^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method`
- --> $DIR/conf_disallowed_methods.rs:58:5
+ --> $DIR/conf_disallowed_methods.rs:60:5
|
LL | s.implemented_method();
| ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
index 179b12661..f267a67f4 100644
--- a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
@@ -1,7 +1,7 @@
//@normalize-stderr-test: "\(\d+ byte\)" -> "(N byte)"
//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)"
-#![deny(clippy::trivially_copy_pass_by_ref)]
+#![warn(clippy::trivially_copy_pass_by_ref)]
#[derive(Copy, Clone)]
struct Foo(u8);
diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
index b3ef5928e..d2b55eff1 100644
--- a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
@@ -4,11 +4,7 @@ error: this argument (N byte) is passed by reference, but would be more efficien
LL | fn bad(x: &u16, y: &Foo) {}
| ^^^^ help: consider passing by value instead: `u16`
|
-note: the lint level is defined here
- --> $DIR/test.rs:4:9
- |
-LL | #![deny(clippy::trivially_copy_pass_by_ref)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: `-D clippy::trivially-copy-pass-by-ref` implied by `-D warnings`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> $DIR/test.rs:14:20
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
index 554b87cc5..b77b45800 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
@@ -1,6 +1,8 @@
# that one is an error
foobar = 42
+# so is this one
+barfoo = 53
-# that one is white-listed
+# that one is ignored
[third-party]
clippy-feature = "nightly"
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
index 569fd2c35..380096277 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
@@ -1,3 +1,3 @@
-//@error-pattern: unknown field `foobar`, expected one of
+//@error-in-other-file: unknown field `foobar`, expected one of
fn main() {}
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 44710b096..6ba26e977 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
@@ -1,9 +1,14 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
+error: error reading Clippy's configuration file: unknown field `foobar`, expected one of
+ accept-comment-above-attributes
+ accept-comment-above-statement
allow-dbg-in-tests
allow-expect-in-tests
allow-mixed-uninlined-format-args
+ allow-one-hash-in-raw-strings
allow-print-in-tests
+ allow-private-module-inception
allow-unwrap-in-tests
+ allowed-idents-below-min-chars
allowed-scripts
arithmetic-side-effects-allowed
arithmetic-side-effects-allowed-binary
@@ -24,6 +29,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
enforced-import-renames
enum-variant-name-threshold
enum-variant-size-threshold
+ excessive-nesting-threshold
future-size-threshold
ignore-interior-mutability
large-error-threshold
@@ -34,12 +40,14 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
max-struct-bools
max-suggested-slice-pattern-length
max-trait-bounds
+ min-ident-chars-threshold
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
semicolon-inside-block-ignore-singleline
semicolon-outside-block-ignore-multiline
single-char-binding-names-threshold
+ stack-size-threshold
standard-macro-braces
suppress-restriction-lint-in-const
third-party
@@ -54,7 +62,79 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
vec-box-size-threshold
verbose-bit-mask-threshold
warn-on-all-wildcard-imports
- at line 5 column 1
+ --> $DIR/$DIR/clippy.toml:2:1
+ |
+LL | foobar = 42
+ | ^^^^^^
-error: aborting due to previous error
+error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of
+ accept-comment-above-attributes
+ accept-comment-above-statement
+ allow-dbg-in-tests
+ allow-expect-in-tests
+ allow-mixed-uninlined-format-args
+ allow-one-hash-in-raw-strings
+ allow-print-in-tests
+ allow-private-module-inception
+ allow-unwrap-in-tests
+ allowed-idents-below-min-chars
+ allowed-scripts
+ arithmetic-side-effects-allowed
+ arithmetic-side-effects-allowed-binary
+ arithmetic-side-effects-allowed-unary
+ array-size-threshold
+ avoid-breaking-exported-api
+ await-holding-invalid-types
+ blacklisted-names
+ cargo-ignore-publish
+ cognitive-complexity-threshold
+ cyclomatic-complexity-threshold
+ disallowed-macros
+ disallowed-methods
+ disallowed-names
+ disallowed-types
+ doc-valid-idents
+ enable-raw-pointer-heuristic-for-send
+ enforced-import-renames
+ enum-variant-name-threshold
+ enum-variant-size-threshold
+ excessive-nesting-threshold
+ future-size-threshold
+ ignore-interior-mutability
+ large-error-threshold
+ literal-representation-threshold
+ matches-for-let-else
+ max-fn-params-bools
+ max-include-file-size
+ max-struct-bools
+ max-suggested-slice-pattern-length
+ max-trait-bounds
+ min-ident-chars-threshold
+ missing-docs-in-crate-items
+ msrv
+ pass-by-value-size-limit
+ semicolon-inside-block-ignore-singleline
+ semicolon-outside-block-ignore-multiline
+ single-char-binding-names-threshold
+ stack-size-threshold
+ standard-macro-braces
+ suppress-restriction-lint-in-const
+ third-party
+ too-large-for-stack
+ too-many-arguments-threshold
+ too-many-lines-threshold
+ trivial-copy-size-limit
+ type-complexity-threshold
+ unnecessary-box-size
+ unreadable-literal-lint-fractions
+ upper-case-acronyms-aggressive
+ vec-box-size-threshold
+ verbose-bit-mask-threshold
+ warn-on-all-wildcard-imports
+ --> $DIR/$DIR/clippy.toml:4:1
+ |
+LL | barfoo = 53
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs
new file mode 100644
index 000000000..1c591fc76
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs
@@ -0,0 +1,13 @@
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn unsafe_block(input: TokenStream) -> TokenStream {
+ let span = input.into_iter().next().unwrap().span();
+ TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(span);
+ TokenTree::Group(group)
+ }])
+}
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml
new file mode 100644
index 000000000..e6dbb3d37
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml
@@ -0,0 +1,2 @@
+accept-comment-above-statement = true
+accept-comment-above-attributes = true
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
new file mode 100644
index 000000000..33d636709
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
@@ -0,0 +1,567 @@
+//@aux-build:proc_macro_unsafe.rs:proc-macro
+
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
+#![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)]
+#![feature(lint_reasons)]
+
+extern crate proc_macro_unsafe;
+
+// Valid comments
+
+fn nested_local() {
+ let _ = {
+ let _ = {
+ // SAFETY:
+ let _ = unsafe {};
+ };
+ };
+}
+
+fn deep_nest() {
+ let _ = {
+ let _ = {
+ // SAFETY:
+ let _ = unsafe {};
+
+ // Safety:
+ unsafe {};
+
+ let _ = {
+ let _ = {
+ let _ = {
+ let _ = {
+ let _ = {
+ // Safety:
+ let _ = unsafe {};
+
+ // SAFETY:
+ unsafe {};
+ };
+ };
+ };
+
+ // Safety:
+ unsafe {};
+ };
+ };
+ };
+
+ // Safety:
+ unsafe {};
+ };
+
+ // SAFETY:
+ unsafe {};
+}
+
+fn local_tuple_expression() {
+ // Safety:
+ let _ = (42, unsafe {});
+}
+
+fn line_comment() {
+ // Safety:
+ unsafe {}
+}
+
+fn line_comment_newlines() {
+ // SAFETY:
+
+ unsafe {}
+}
+
+fn line_comment_empty() {
+ // Safety:
+ //
+ //
+ //
+ unsafe {}
+}
+
+fn line_comment_with_extras() {
+ // This is a description
+ // Safety:
+ unsafe {}
+}
+
+fn block_comment() {
+ /* Safety: */
+ unsafe {}
+}
+
+fn block_comment_newlines() {
+ /* SAFETY: */
+
+ unsafe {}
+}
+
+fn block_comment_with_extras() {
+ /* This is a description
+ * SAFETY:
+ */
+ unsafe {}
+}
+
+fn block_comment_terminator_same_line() {
+ /* This is a description
+ * Safety: */
+ unsafe {}
+}
+
+fn buried_safety() {
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+ // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+ // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ // laborum. Safety:
+ // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
+ // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
+ // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
+ // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
+ unsafe {}
+}
+
+fn safety_with_prepended_text() {
+ // This is a test. safety:
+ unsafe {}
+}
+
+fn local_line_comment() {
+ // Safety:
+ let _ = unsafe {};
+}
+
+fn local_block_comment() {
+ /* SAFETY: */
+ let _ = unsafe {};
+}
+
+fn comment_array() {
+ // Safety:
+ let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+}
+
+fn comment_tuple() {
+ // sAFETY:
+ let _ = (42, unsafe {}, "test", unsafe {});
+}
+
+fn comment_unary() {
+ // SAFETY:
+ let _ = *unsafe { &42 };
+}
+
+#[allow(clippy::match_single_binding)]
+fn comment_match() {
+ // SAFETY:
+ let _ = match unsafe {} {
+ _ => {},
+ };
+}
+
+fn comment_addr_of() {
+ // Safety:
+ let _ = &unsafe {};
+}
+
+fn comment_repeat() {
+ // Safety:
+ let _ = [unsafe {}; 5];
+}
+
+fn comment_macro_call() {
+ macro_rules! t {
+ ($b:expr) => {
+ $b
+ };
+ }
+
+ t!(
+ // SAFETY:
+ unsafe {}
+ );
+}
+
+fn comment_macro_def() {
+ macro_rules! t {
+ () => {
+ // Safety:
+ unsafe {}
+ };
+ }
+
+ t!();
+}
+
+fn non_ascii_comment() {
+ // ॐ᧻໒ SaFeTy: ௵∰
+ unsafe {};
+}
+
+fn local_commented_block() {
+ let _ =
+ // safety:
+ unsafe {};
+}
+
+fn local_nest() {
+ // safety:
+ let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
+}
+
+fn in_fn_call(x: *const u32) {
+ fn f(x: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x });
+}
+
+fn multi_in_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x }, unsafe { *x });
+}
+
+fn in_multiline_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ f(
+ // Safety: reason
+ unsafe { *x },
+ 0,
+ );
+}
+
+fn in_macro_call(x: *const u32) {
+ // Safety: reason
+ println!("{}", unsafe { *x });
+}
+
+fn in_multiline_macro_call(x: *const u32) {
+ println!(
+ "{}",
+ // Safety: reason
+ unsafe { *x },
+ );
+}
+
+fn from_proc_macro() {
+ proc_macro_unsafe::unsafe_block!(token);
+}
+
+fn in_closure(x: *const u32) {
+ // Safety: reason
+ let _ = || unsafe { *x };
+}
+
+// Invalid comments
+
+#[rustfmt::skip]
+fn inline_block_comment() {
+ /* Safety: */ unsafe {}
+}
+
+fn no_comment() {
+ unsafe {}
+}
+
+fn no_comment_array() {
+ let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+}
+
+fn no_comment_tuple() {
+ let _ = (42, unsafe {}, "test", unsafe {});
+}
+
+fn no_comment_unary() {
+ let _ = *unsafe { &42 };
+}
+
+#[allow(clippy::match_single_binding)]
+fn no_comment_match() {
+ let _ = match unsafe {} {
+ _ => {},
+ };
+}
+
+fn no_comment_addr_of() {
+ let _ = &unsafe {};
+}
+
+fn no_comment_repeat() {
+ let _ = [unsafe {}; 5];
+}
+
+fn local_no_comment() {
+ let _ = unsafe {};
+}
+
+fn no_comment_macro_call() {
+ macro_rules! t {
+ ($b:expr) => {
+ $b
+ };
+ }
+
+ t!(unsafe {});
+}
+
+fn no_comment_macro_def() {
+ macro_rules! t {
+ () => {
+ unsafe {}
+ };
+ }
+
+ t!();
+}
+
+fn trailing_comment() {
+ unsafe {} // SAFETY:
+}
+
+fn internal_comment() {
+ unsafe {
+ // SAFETY:
+ }
+}
+
+fn interference() {
+ // SAFETY
+
+ let _ = 42;
+
+ unsafe {};
+}
+
+pub fn print_binary_tree() {
+ println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+}
+
+mod unsafe_impl_smoke_test {
+ unsafe trait A {}
+
+ // error: no safety comment
+ unsafe impl A for () {}
+
+ // Safety: ok
+ unsafe impl A for (i32) {}
+
+ mod sub_mod {
+ // error:
+ unsafe impl B for (u32) {}
+ unsafe trait B {}
+ }
+
+ #[rustfmt::skip]
+ mod sub_mod2 {
+ //
+ // SAFETY: ok
+ //
+
+ unsafe impl B for (u32) {}
+ unsafe trait B {}
+ }
+}
+
+mod unsafe_impl_from_macro {
+ unsafe trait T {}
+
+ // error
+ macro_rules! no_safety_comment {
+ ($t:ty) => {
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ no_safety_comment!(());
+
+ // ok
+ macro_rules! with_safety_comment {
+ ($t:ty) => {
+ // SAFETY:
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ with_safety_comment!((i32));
+}
+
+mod unsafe_impl_macro_and_not_macro {
+ unsafe trait T {}
+
+ // error
+ macro_rules! no_safety_comment {
+ ($t:ty) => {
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ no_safety_comment!(());
+
+ // error
+ unsafe impl T for (i32) {}
+
+ // ok
+ no_safety_comment!(u32);
+
+ // error
+ unsafe impl T for (bool) {}
+}
+
+#[rustfmt::skip]
+mod unsafe_impl_valid_comment {
+ unsafe trait SaFety {}
+ // SaFety:
+ unsafe impl SaFety for () {}
+
+ unsafe trait MultiLineComment {}
+ // The following impl is safe
+ // ...
+ // Safety: reason
+ unsafe impl MultiLineComment for () {}
+
+ unsafe trait NoAscii {}
+ // 安全 SAFETY: 以下のコードは安全です
+ unsafe impl NoAscii for () {}
+
+ unsafe trait InlineAndPrecedingComment {}
+ // SAFETY:
+ /* comment */ unsafe impl InlineAndPrecedingComment for () {}
+
+ unsafe trait BuriedSafety {}
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+ // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+ // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ // laborum. Safety:
+ // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
+ // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
+ // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
+ // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
+ unsafe impl BuriedSafety for () {}
+
+ unsafe trait MultiLineBlockComment {}
+ /* This is a description
+ * Safety: */
+ unsafe impl MultiLineBlockComment for () {}
+}
+
+#[rustfmt::skip]
+mod unsafe_impl_invalid_comment {
+ unsafe trait NoComment {}
+
+ unsafe impl NoComment for () {}
+
+ unsafe trait InlineComment {}
+
+ /* SAFETY: */ unsafe impl InlineComment for () {}
+
+ unsafe trait TrailingComment {}
+
+ unsafe impl TrailingComment for () {} // SAFETY:
+
+ unsafe trait Interference {}
+ // SAFETY:
+ const BIG_NUMBER: i32 = 1000000;
+ unsafe impl Interference for () {}
+}
+
+unsafe trait ImplInFn {}
+
+fn impl_in_fn() {
+ // error
+ unsafe impl ImplInFn for () {}
+
+ // SAFETY: ok
+ unsafe impl ImplInFn for (i32) {}
+}
+
+unsafe trait CrateRoot {}
+
+// error
+unsafe impl CrateRoot for () {}
+
+// SAFETY: ok
+unsafe impl CrateRoot for (i32) {}
+
+fn issue_9142() {
+ // SAFETY: ok
+ let _ =
+ // we need this comment to avoid rustfmt putting
+ // it all on one line
+ unsafe {};
+
+ // SAFETY: this is more than one level away, so it should warn
+ let _ = {
+ if unsafe { true } {
+ todo!();
+ } else {
+ let bar = unsafe {};
+ todo!();
+ bar
+ }
+ };
+}
+
+pub unsafe fn a_function_with_a_very_long_name_to_break_the_line() -> u32 {
+ 1
+}
+
+pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() -> u32 {
+ 2
+}
+
+fn issue_10832() {
+ // Safety: A safety comment
+ let _some_variable_with_a_very_long_name_to_break_the_line =
+ unsafe { a_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Another safety comment
+ const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Yet another safety comment
+ static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+}
+
+fn issue_8679<T: Copy>() {
+ // SAFETY:
+ #[allow(unsafe_code)]
+ unsafe {}
+
+ // SAFETY:
+ #[expect(unsafe_code, reason = "totally safe")]
+ unsafe {
+ *std::ptr::null::<T>()
+ };
+
+ // Safety: A safety comment
+ #[allow(unsafe_code)]
+ let _some_variable_with_a_very_long_name_to_break_the_line =
+ unsafe { a_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Another safety comment
+ #[allow(unsafe_code)]
+ const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Yet another safety comment
+ #[allow(unsafe_code)]
+ static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+
+ // SAFETY:
+ #[allow(unsafe_code)]
+ // This also works I guess
+ unsafe {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr
new file mode 100644
index 000000000..9a0fd0593
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr
@@ -0,0 +1,314 @@
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:263:19
+ |
+LL | /* Safety: */ unsafe {}
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:267:5
+ |
+LL | unsafe {}
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:271:14
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:271:29
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:271:48
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:275:18
+ |
+LL | let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:275:37
+ |
+LL | let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:279:14
+ |
+LL | let _ = *unsafe { &42 };
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:284:19
+ |
+LL | let _ = match unsafe {} {
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:290:14
+ |
+LL | let _ = &unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:294:14
+ |
+LL | let _ = [unsafe {}; 5];
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:298:13
+ |
+LL | let _ = unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:308:8
+ |
+LL | t!(unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:314:13
+ |
+LL | unsafe {}
+ | ^^^^^^^^^
+...
+LL | t!();
+ | ---- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:322:5
+ |
+LL | unsafe {} // SAFETY:
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:326:5
+ |
+LL | unsafe {
+ | ^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:336:5
+ |
+LL | unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:340:20
+ |
+LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:347:5
+ |
+LL | unsafe impl A for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:354:9
+ |
+LL | unsafe impl B for (u32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:375:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(());
+ | ---------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:400:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(());
+ | ---------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:408:5
+ |
+LL | unsafe impl T for (i32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:400:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(u32);
+ | ----------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:414:5
+ |
+LL | unsafe impl T for (bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:460:5
+ |
+LL | unsafe impl NoComment for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:464:19
+ |
+LL | /* SAFETY: */ unsafe impl InlineComment for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:468:5
+ |
+LL | unsafe impl TrailingComment for () {} // SAFETY:
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: constant item has unnecessary safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:472:5
+ |
+LL | const BIG_NUMBER: i32 = 1000000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:471:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:473:5
+ |
+LL | unsafe impl Interference for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:480:5
+ |
+LL | unsafe impl ImplInFn for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:489:1
+ |
+LL | unsafe impl CrateRoot for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: statement has unnecessary safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:502:5
+ |
+LL | / let _ = {
+LL | | if unsafe { true } {
+LL | | todo!();
+LL | | } else {
+... |
+LL | | }
+LL | | };
+ | |______^
+ |
+help: consider removing the safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:501:5
+ |
+LL | // SAFETY: this is more than one level away, so it should warn
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:503:12
+ |
+LL | if unsafe { true } {
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:506:23
+ |
+LL | let bar = unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: aborting due to 35 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
index 5d3e800ca..dde1c6d7c 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
@@ -1,8 +1,13 @@
//@compile-flags: --test
-#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)]
+#![allow(
+ unused_mut,
+ clippy::get_first,
+ clippy::from_iter_instead_of_collect,
+ clippy::useless_vec
+)]
#![warn(clippy::unwrap_used)]
-#![deny(clippy::get_unwrap)]
+#![warn(clippy::get_unwrap)]
use std::collections::BTreeMap;
use std::collections::HashMap;
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
index 8a32750e3..eb66a5cf5 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -1,17 +1,13 @@
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:35:17
+ --> $DIR/unwrap_used.rs:40:17
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
|
-note: the lint level is defined here
- --> $DIR/unwrap_used.rs:5:9
- |
-LL | #![deny(clippy::get_unwrap)]
- | ^^^^^^^^^^^^^^^^^^
+ = note: `-D clippy::get-unwrap` implied by `-D warnings`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:35:17
+ --> $DIR/unwrap_used.rs:40:17
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -20,13 +16,13 @@ LL | let _ = boxed_slice.get(1).unwrap();
= note: `-D clippy::unwrap-used` implied by `-D warnings`
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:36:17
+ --> $DIR/unwrap_used.rs:41:17
|
LL | let _ = some_slice.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:36:17
+ --> $DIR/unwrap_used.rs:41:17
|
LL | let _ = some_slice.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,13 +30,13 @@ LL | let _ = some_slice.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:37:17
+ --> $DIR/unwrap_used.rs:42:17
|
LL | let _ = some_vec.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:37:17
+ --> $DIR/unwrap_used.rs:42:17
|
LL | let _ = some_vec.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,13 +44,13 @@ LL | let _ = some_vec.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:38:17
+ --> $DIR/unwrap_used.rs:43:17
|
LL | let _ = some_vecdeque.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:38:17
+ --> $DIR/unwrap_used.rs:43:17
|
LL | let _ = some_vecdeque.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,13 +58,13 @@ LL | let _ = some_vecdeque.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:39:17
+ --> $DIR/unwrap_used.rs:44:17
|
LL | let _ = some_hashmap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:39:17
+ --> $DIR/unwrap_used.rs:44:17
|
LL | let _ = some_hashmap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -76,13 +72,13 @@ LL | let _ = some_hashmap.get(&1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:40:17
+ --> $DIR/unwrap_used.rs:45:17
|
LL | let _ = some_btreemap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:40:17
+ --> $DIR/unwrap_used.rs:45:17
|
LL | let _ = some_btreemap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -90,13 +86,13 @@ LL | let _ = some_btreemap.get(&1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:44:21
+ --> $DIR/unwrap_used.rs:49:21
|
LL | let _: u8 = *boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:44:22
+ --> $DIR/unwrap_used.rs:49:22
|
LL | let _: u8 = *boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,13 +100,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:49:9
+ --> $DIR/unwrap_used.rs:54:9
|
LL | *boxed_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:49:10
+ --> $DIR/unwrap_used.rs:54:10
|
LL | *boxed_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,13 +114,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:50:9
+ --> $DIR/unwrap_used.rs:55:9
|
LL | *some_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:50:10
+ --> $DIR/unwrap_used.rs:55:10
|
LL | *some_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,13 +128,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:51:9
+ --> $DIR/unwrap_used.rs:56:9
|
LL | *some_vec.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:51:10
+ --> $DIR/unwrap_used.rs:56:10
|
LL | *some_vec.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -146,13 +142,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:52:9
+ --> $DIR/unwrap_used.rs:57:9
|
LL | *some_vecdeque.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:52:10
+ --> $DIR/unwrap_used.rs:57:10
|
LL | *some_vecdeque.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -160,13 +156,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:64:17
+ --> $DIR/unwrap_used.rs:69:17
|
LL | let _ = some_vec.get(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:64:17
+ --> $DIR/unwrap_used.rs:69:17
|
LL | let _ = some_vec.get(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -174,13 +170,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:65:17
+ --> $DIR/unwrap_used.rs:70:17
|
LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_used.rs:65:17
+ --> $DIR/unwrap_used.rs:70:17
|
LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -188,13 +184,13 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:72:13
+ --> $DIR/unwrap_used.rs:77:13
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/unwrap_used.rs:90:17
+ --> $DIR/unwrap_used.rs:95:17
|
LL | let _ = Box::new([0]).get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]`
diff --git a/src/tools/clippy/tests/ui-toml/update-all-references.sh b/src/tools/clippy/tests/ui-toml/update-all-references.sh
index 4391499a1..d42043070 100755
--- a/src/tools/clippy/tests/ui-toml/update-all-references.sh
+++ b/src/tools/clippy/tests/ui-toml/update-all-references.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-echo "Please use 'cargo dev bless' instead."
+echo "Please use 'cargo bless' instead."
diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed
index f0936b260..cc95a0681 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.fixed
+++ b/src/tools/clippy/tests/ui/allow_attributes.fixed
@@ -1,9 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::allow_attributes)]
#![feature(lint_reasons)]
+#![no_main]
-fn main() {}
+extern crate proc_macros;
+use proc_macros::{external, with_span};
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
@@ -20,6 +23,21 @@ struct T4;
#[cfg_attr(panic = "unwind", expect(dead_code))]
struct CfgT;
+fn ignore_external() {
+ external! {
+ #[allow(clippy::needless_borrow)] // Should not lint
+ fn a() {}
+ }
+}
+
+fn ignore_proc_macro() {
+ with_span! {
+ span
+ #[allow(clippy::needless_borrow)] // Should not lint
+ fn a() {}
+ }
+}
+
fn ignore_inner_attr() {
#![allow(unused)] // Should not lint
}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs
index 2fb9e8612..2eb6ad304 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes.rs
@@ -1,9 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::allow_attributes)]
#![feature(lint_reasons)]
+#![no_main]
-fn main() {}
+extern crate proc_macros;
+use proc_macros::{external, with_span};
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
@@ -20,6 +23,21 @@ struct T4;
#[cfg_attr(panic = "unwind", allow(dead_code))]
struct CfgT;
+fn ignore_external() {
+ external! {
+ #[allow(clippy::needless_borrow)] // Should not lint
+ fn a() {}
+ }
+}
+
+fn ignore_proc_macro() {
+ with_span! {
+ span
+ #[allow(clippy::needless_borrow)] // Should not lint
+ fn a() {}
+ }
+}
+
fn ignore_inner_attr() {
#![allow(unused)] // Should not lint
}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr
index 681837e9e..d17fd86cb 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.stderr
+++ b/src/tools/clippy/tests/ui/allow_attributes.stderr
@@ -1,5 +1,5 @@
error: #[allow] attribute found
- --> $DIR/allow_attributes.rs:11:3
+ --> $DIR/allow_attributes.rs:14:3
|
LL | #[allow(dead_code)]
| ^^^^^ help: replace it with: `expect`
@@ -7,7 +7,7 @@ LL | #[allow(dead_code)]
= note: `-D clippy::allow-attributes` implied by `-D warnings`
error: #[allow] attribute found
- --> $DIR/allow_attributes.rs:20:30
+ --> $DIR/allow_attributes.rs:23:30
|
LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
| ^^^^^ help: replace it with: `expect`
diff --git a/src/tools/clippy/tests/ui/allow_attributes_false_positive.rs b/src/tools/clippy/tests/ui/allow_attributes_false_positive.rs
deleted file mode 100644
index 5c3407628..000000000
--- a/src/tools/clippy/tests/ui/allow_attributes_false_positive.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#![warn(clippy::allow_attributes)]
-#![feature(lint_reasons)]
-#![crate_type = "proc-macro"]
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
index 1a0d4e886..d223d5642 100644
--- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
@@ -1,9 +1,15 @@
+//@aux-build:proc_macros.rs:proc-macro
#![feature(lint_reasons)]
#![deny(clippy::allow_attributes_without_reason)]
+#![allow(unfulfilled_lint_expectations)]
+
+extern crate proc_macros;
+use proc_macros::{external, with_span};
// These should trigger the lint
#[allow(dead_code)]
#[allow(dead_code, deprecated)]
+#[expect(dead_code)]
// These should be fine
#[allow(dead_code, reason = "This should be allowed")]
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
@@ -11,4 +17,28 @@
#[deny(deref_nullptr)]
#[forbid(deref_nullptr)]
-fn main() {}
+fn main() {
+ external! {
+ #[allow(dead_code)]
+ fn a() {}
+ }
+ with_span! {
+ span
+ #[allow(dead_code)]
+ fn b() {}
+ }
+}
+
+// Make sure this is not triggered on `?` desugaring
+
+pub fn trigger_fp_option() -> Option<()> {
+ Some(())?;
+ None?;
+ Some(())
+}
+
+pub fn trigger_fp_result() -> Result<(), &'static str> {
+ Ok(())?;
+ Err("asdf")?;
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
index 23f17e9a7..96f747d00 100644
--- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
@@ -1,23 +1,39 @@
error: `allow` attribute without specifying a reason
- --> $DIR/allow_attributes_without_reason.rs:5:1
+ --> $DIR/allow_attributes_without_reason.rs:4:1
|
-LL | #[allow(dead_code)]
- | ^^^^^^^^^^^^^^^^^^^
+LL | #![allow(unfulfilled_lint_expectations)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try adding a reason at the end with `, reason = ".."`
note: the lint level is defined here
- --> $DIR/allow_attributes_without_reason.rs:2:9
+ --> $DIR/allow_attributes_without_reason.rs:3:9
|
LL | #![deny(clippy::allow_attributes_without_reason)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `allow` attribute without specifying a reason
- --> $DIR/allow_attributes_without_reason.rs:6:1
+ --> $DIR/allow_attributes_without_reason.rs:10:1
+ |
+LL | #[allow(dead_code)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a reason at the end with `, reason = ".."`
+
+error: `allow` attribute without specifying a reason
+ --> $DIR/allow_attributes_without_reason.rs:11:1
|
LL | #[allow(dead_code, deprecated)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try adding a reason at the end with `, reason = ".."`
-error: aborting due to 2 previous errors
+error: `expect` attribute without specifying a reason
+ --> $DIR/allow_attributes_without_reason.rs:12:1
+ |
+LL | #[expect(dead_code)]
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a reason at the end with `, reason = ".."`
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/almost_complete_range.fixed b/src/tools/clippy/tests/ui/almost_complete_range.fixed
index 5cd0dcce6..50a13f16b 100644
--- a/src/tools/clippy/tests/ui/almost_complete_range.fixed
+++ b/src/tools/clippy/tests/ui/almost_complete_range.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
//@edition:2018
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
diff --git a/src/tools/clippy/tests/ui/almost_complete_range.rs b/src/tools/clippy/tests/ui/almost_complete_range.rs
index db0bfc8af..fd8223a23 100644
--- a/src/tools/clippy/tests/ui/almost_complete_range.rs
+++ b/src/tools/clippy/tests/ui/almost_complete_range.rs
@@ -1,6 +1,6 @@
//@run-rustfix
//@edition:2018
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
new file mode 100644
index 000000000..b6fcca0a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::arc_with_non_send_sync)]
+#![allow(unused_variables)]
+use std::cell::RefCell;
+use std::sync::{Arc, Mutex};
+
+fn foo<T>(x: T) {
+ // Should not lint - purposefully ignoring generic args.
+ let a = Arc::new(x);
+}
+fn issue11076<T>() {
+ let a: Arc<Vec<T>> = Arc::new(Vec::new());
+}
+
+fn main() {
+ let _ = Arc::new(42);
+
+ // !Sync
+ let _ = Arc::new(RefCell::new(42));
+ let mutex = Mutex::new(1);
+ // !Send
+ let _ = Arc::new(mutex.lock().unwrap());
+ // !Send + !Sync
+ let _ = Arc::new(&42 as *const i32);
+}
diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
new file mode 100644
index 000000000..7633b38df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
@@ -0,0 +1,34 @@
+error: usage of an `Arc` that is not `Send` or `Sync`
+ --> $DIR/arc_with_non_send_sync.rs:18:13
+ |
+LL | let _ = Arc::new(RefCell::new(42));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: required for `Arc<RefCell<i32>>` to implement `Send` and `Sync`
+ = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`
+ = note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings`
+
+error: usage of an `Arc` that is not `Send` or `Sync`
+ --> $DIR/arc_with_non_send_sync.rs:21:13
+ |
+LL | let _ = Arc::new(mutex.lock().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the trait `Send` is not implemented for `MutexGuard<'_, i32>`
+ = note: required for `Arc<MutexGuard<'_, i32>>` to implement `Send` and `Sync`
+ = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`
+
+error: usage of an `Arc` that is not `Send` or `Sync`
+ --> $DIR/arc_with_non_send_sync.rs:23:13
+ |
+LL | let _ = Arc::new(&42 as *const i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the trait `Send` is not implemented for `*const i32`
+ = note: the trait `Sync` is not implemented for `*const i32`
+ = note: required for `Arc<*const i32>` to implement `Send` and `Sync`
+ = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index f95af1017..4f38e50c8 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![allow(
clippy::assign_op_pattern,
@@ -466,4 +466,19 @@ pub fn issue_10767() {
&3.5_f32 + &1.3_f32;
}
+pub fn issue_10792() {
+ struct One {
+ a: u32,
+ }
+ struct Two {
+ b: u32,
+ c: u64,
+ }
+ const ONE: One = One { a: 1 };
+ const TWO: Two = Two { b: 2, c: 3 };
+ let _ = 10 / ONE.a;
+ let _ = 10 / TWO.b;
+ let _ = 10 / TWO.c;
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs
index 890bf0b0a..427842a51 100644
--- a/src/tools/clippy/tests/ui/as_conversions.rs
+++ b/src/tools/clippy/tests/ui/as_conversions.rs
@@ -1,10 +1,11 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::as_conversions)]
-#![allow(clippy::borrow_as_ptr)]
+#![allow(clippy::borrow_as_ptr, unused)]
extern crate proc_macros;
use proc_macros::external;
+use proc_macros::with_span;
fn main() {
let i = 0u32 as u64;
@@ -13,3 +14,11 @@ fn main() {
external!(0u32 as u64);
}
+
+with_span!(
+ span
+
+ fn coverting() {
+ let x = 0u32 as u64;
+ }
+);
diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr
index 54037a649..ca41d1378 100644
--- a/src/tools/clippy/tests/ui/as_conversions.stderr
+++ b/src/tools/clippy/tests/ui/as_conversions.stderr
@@ -1,5 +1,5 @@
error: using a potentially dangerous silent `as` conversion
- --> $DIR/as_conversions.rs:10:13
+ --> $DIR/as_conversions.rs:11:13
|
LL | let i = 0u32 as u64;
| ^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let i = 0u32 as u64;
= note: `-D clippy::as-conversions` implied by `-D warnings`
error: using a potentially dangerous silent `as` conversion
- --> $DIR/as_conversions.rs:12:13
+ --> $DIR/as_conversions.rs:13:13
|
LL | let j = &i as *const u64 as *mut u64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64;
= help: consider using a safe wrapper for this conversion
error: using a potentially dangerous silent `as` conversion
- --> $DIR/as_conversions.rs:12:13
+ --> $DIR/as_conversions.rs:13:13
|
LL | let j = &i as *const u64 as *mut u64;
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs b/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
index 0d1d92584..7d71947e4 100644
--- a/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
@@ -1,6 +1,6 @@
#![allow(unused)]
#![warn(clippy::as_ptr_cast_mut)]
-#![allow(clippy::wrong_self_convention)]
+#![allow(clippy::wrong_self_convention, clippy::unnecessary_cast)]
struct MutPtrWrapper(Vec<u8>);
impl MutPtrWrapper {
diff --git a/src/tools/clippy/tests/ui/asm_syntax.rs b/src/tools/clippy/tests/ui/asm_syntax.rs
index c93995f93..af02e202b 100644
--- a/src/tools/clippy/tests/ui/asm_syntax.rs
+++ b/src/tools/clippy/tests/ui/asm_syntax.rs
@@ -1,5 +1,5 @@
-//@only-x86_64
-//@ignore-aarch64
+//@only-target-x86_64
+//@ignore-target-aarch64
#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
index ea8b89566..3152bd3ca 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::assertions_on_result_states)]
+#![allow(clippy::unnecessary_literal_unwrap)]
use std::result::Result;
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.rs b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
index 6fc20f859..42755e935 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.rs
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::assertions_on_result_states)]
+#![allow(clippy::unnecessary_literal_unwrap)]
use std::result::Result;
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
index 298d63c9c..be581030c 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
@@ -1,5 +1,5 @@
error: called `assert!` with `Result::is_ok`
- --> $DIR/assertions_on_result_states.rs:24:5
+ --> $DIR/assertions_on_result_states.rs:25:5
|
LL | assert!(r.is_ok());
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
@@ -7,37 +7,37 @@ LL | assert!(r.is_ok());
= note: `-D clippy::assertions-on-result-states` implied by `-D warnings`
error: called `assert!` with `Result::is_ok`
- --> $DIR/assertions_on_result_states.rs:42:5
+ --> $DIR/assertions_on_result_states.rs:43:5
|
LL | assert!(get_ok().is_ok());
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok().unwrap()`
error: called `assert!` with `Result::is_ok`
- --> $DIR/assertions_on_result_states.rs:45:5
+ --> $DIR/assertions_on_result_states.rs:46:5
|
LL | assert!(get_ok_macro!().is_ok());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok_macro!().unwrap()`
error: called `assert!` with `Result::is_ok`
- --> $DIR/assertions_on_result_states.rs:58:5
+ --> $DIR/assertions_on_result_states.rs:59:5
|
LL | assert!(r.is_ok());
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
error: called `assert!` with `Result::is_ok`
- --> $DIR/assertions_on_result_states.rs:64:9
+ --> $DIR/assertions_on_result_states.rs:65:9
|
LL | assert!(r.is_ok());
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
error: called `assert!` with `Result::is_err`
- --> $DIR/assertions_on_result_states.rs:72:5
+ --> $DIR/assertions_on_result_states.rs:73:5
|
LL | assert!(r.is_err());
| ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
error: called `assert!` with `Result::is_err`
- --> $DIR/assertions_on_result_states.rs:82:5
+ --> $DIR/assertions_on_result_states.rs:83:5
|
LL | assert!(res.is_err())
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();`
diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed
index b50682ea0..ef45e97d1 100644
--- a/src/tools/clippy/tests/ui/assign_ops.fixed
+++ b/src/tools/clippy/tests/ui/assign_ops.fixed
@@ -2,7 +2,7 @@
use core::num::Wrapping;
-#[allow(dead_code, unused_assignments)]
+#[allow(dead_code, unused_assignments, clippy::useless_vec)]
#[warn(clippy::assign_op_pattern)]
fn main() {
let mut a = 5;
diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs
index 780d2d040..ae87afc48 100644
--- a/src/tools/clippy/tests/ui/assign_ops.rs
+++ b/src/tools/clippy/tests/ui/assign_ops.rs
@@ -2,7 +2,7 @@
use core::num::Wrapping;
-#[allow(dead_code, unused_assignments)]
+#[allow(dead_code, unused_assignments, clippy::useless_vec)]
#[warn(clippy::assign_op_pattern)]
fn main() {
let mut a = 5;
diff --git a/src/tools/clippy/tests/ui/auxiliary/extern_fake_libc.rs b/src/tools/clippy/tests/ui/auxiliary/extern_fake_libc.rs
new file mode 100644
index 000000000..eb5a5d2b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/extern_fake_libc.rs
@@ -0,0 +1,10 @@
+#![allow(nonstandard_style)]
+#![allow(clippy::missing_safety_doc, unused)]
+
+type pid_t = i32;
+pub unsafe fn getpid() -> pid_t {
+ pid_t::from(0)
+}
+pub fn getpid_SAFE_TRUTH() -> pid_t {
+ unsafe { getpid() }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
index e5bb90666..6b164967a 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
@@ -1,7 +1,6 @@
#![allow(dead_code)]
//! Used to test that certain lints don't trigger in imported external macros
-
#[macro_export]
macro_rules! try_err {
() => {
@@ -44,3 +43,10 @@ macro_rules! issue_10421 {
b = a;
};
}
+
+#[macro_export]
+macro_rules! macro_with_panic {
+ () => {
+ panic!()
+ };
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
index 7ed8a28db..cab216b51 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
@@ -1,3 +1,5 @@
+//@aux-build:macro_rules.rs
+
extern crate macro_rules;
// STMT
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
index d164dd0e5..fdfe5fc41 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
@@ -1,7 +1,3 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
#![allow(incomplete_features)]
#![allow(clippy::useless_conversion, clippy::uninlined_format_args)]
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 5a924ca18..37f0ec2b3 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -1,7 +1,3 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_quote)]
#![allow(incomplete_features)]
#![allow(clippy::field_reassign_with_default)]
@@ -90,70 +86,58 @@ pub fn extra_lifetime(_input: TokenStream) -> TokenStream {
#[allow(unused)]
#[proc_macro_derive(ArithmeticDerive)]
pub fn arithmetic_derive(_: TokenStream) -> TokenStream {
- <TokenStream as FromIterator<TokenTree>>::from_iter(
- [
- Ident::new("fn", Span::call_site()).into(),
- Ident::new("_foo", Span::call_site()).into(),
- Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
- Group::new(
- Delimiter::Brace,
- <TokenStream as FromIterator<TokenTree>>::from_iter(
- [
- Ident::new("let", Span::call_site()).into(),
- Ident::new("mut", Span::call_site()).into(),
- Ident::new("_n", Span::call_site()).into(),
- Punct::new('=', Spacing::Alone).into(),
- Literal::i32_unsuffixed(9).into(),
- Punct::new(';', Spacing::Alone).into(),
- Ident::new("_n", Span::call_site()).into(),
- Punct::new('=', Spacing::Alone).into(),
- Literal::i32_unsuffixed(9).into(),
- Punct::new('/', Spacing::Alone).into(),
- Literal::i32_unsuffixed(2).into(),
- Punct::new(';', Spacing::Alone).into(),
- Ident::new("_n", Span::call_site()).into(),
- Punct::new('=', Spacing::Alone).into(),
- Punct::new('-', Spacing::Alone).into(),
- Ident::new("_n", Span::call_site()).into(),
- Punct::new(';', Spacing::Alone).into(),
- ]
- .into_iter(),
- ),
- )
- .into(),
- ]
- .into_iter(),
- )
+ <TokenStream as FromIterator<TokenTree>>::from_iter([
+ Ident::new("fn", Span::call_site()).into(),
+ Ident::new("_foo", Span::call_site()).into(),
+ Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter([
+ Ident::new("let", Span::call_site()).into(),
+ Ident::new("mut", Span::call_site()).into(),
+ Ident::new("_n", Span::call_site()).into(),
+ Punct::new('=', Spacing::Alone).into(),
+ Literal::i32_unsuffixed(9).into(),
+ Punct::new(';', Spacing::Alone).into(),
+ Ident::new("_n", Span::call_site()).into(),
+ Punct::new('=', Spacing::Alone).into(),
+ Literal::i32_unsuffixed(9).into(),
+ Punct::new('/', Spacing::Alone).into(),
+ Literal::i32_unsuffixed(2).into(),
+ Punct::new(';', Spacing::Alone).into(),
+ Ident::new("_n", Span::call_site()).into(),
+ Punct::new('=', Spacing::Alone).into(),
+ Punct::new('-', Spacing::Alone).into(),
+ Ident::new("_n", Span::call_site()).into(),
+ Punct::new(';', Spacing::Alone).into(),
+ ]),
+ )
+ .into(),
+ ])
}
#[allow(unused)]
#[proc_macro_derive(ShadowDerive)]
pub fn shadow_derive(_: TokenStream) -> TokenStream {
- <TokenStream as FromIterator<TokenTree>>::from_iter(
- [
- Ident::new("fn", Span::call_site()).into(),
- Ident::new("_foo", Span::call_site()).into(),
- Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
- Group::new(
- Delimiter::Brace,
- <TokenStream as FromIterator<TokenTree>>::from_iter(
- [
- Ident::new("let", Span::call_site()).into(),
- Ident::new("_x", Span::call_site()).into(),
- Punct::new('=', Spacing::Alone).into(),
- Literal::i32_unsuffixed(2).into(),
- Punct::new(';', Spacing::Alone).into(),
- Ident::new("let", Span::call_site()).into(),
- Ident::new("_x", Span::call_site()).into(),
- Punct::new('=', Spacing::Alone).into(),
- Ident::new("_x", Span::call_site()).into(),
- Punct::new(';', Spacing::Alone).into(),
- ]
- .into_iter(),
- ),
- )
- .into(),
- ]
- .into_iter(),
- )
+ <TokenStream as FromIterator<TokenTree>>::from_iter([
+ Ident::new("fn", Span::call_site()).into(),
+ Ident::new("_foo", Span::call_site()).into(),
+ Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter([
+ Ident::new("let", Span::call_site()).into(),
+ Ident::new("_x", Span::call_site()).into(),
+ Punct::new('=', Spacing::Alone).into(),
+ Literal::i32_unsuffixed(2).into(),
+ Punct::new(';', Spacing::Alone).into(),
+ Ident::new("let", Span::call_site()).into(),
+ Ident::new("_x", Span::call_site()).into(),
+ Punct::new('=', Spacing::Alone).into(),
+ Ident::new("_x", Span::call_site()).into(),
+ Punct::new(';', Spacing::Alone).into(),
+ ]),
+ )
+ .into(),
+ ])
}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
index f13b76e44..79e8eff3a 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
@@ -1,8 +1,3 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
extern crate proc_macro;
use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs
index c2326678d..1c591fc76 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs
@@ -1,8 +1,3 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
extern crate proc_macro;
use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
index 94f075ed0..4d008c8cb 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
@@ -1,10 +1,6 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
#![feature(let_chains)]
#![feature(proc_macro_span)]
-#![allow(dead_code)]
+#![allow(clippy::needless_if, dead_code)]
extern crate proc_macro;
diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
index d75cdd625..44f49c080 100644
--- a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
@@ -25,3 +25,9 @@ pub mod prelude {
pub struct PreludeModAnywhere;
}
}
+
+pub mod extern_prelude {
+ pub mod v1 {
+ pub struct ExternPreludeModAnywhere;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
index a9f18782e..2a3867ac8 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::blocks_in_if_conditions)]
-#![allow(unused, clippy::let_and_return)]
+#![allow(unused, clippy::let_and_return, clippy::needless_if)]
#![warn(clippy::nonminimal_bool)]
macro_rules! blocky {
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
index 0a70317c4..704d09fba 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::blocks_in_if_conditions)]
-#![allow(unused, clippy::let_and_return)]
+#![allow(unused, clippy::let_and_return, clippy::needless_if)]
#![warn(clippy::nonminimal_bool)]
macro_rules! blocky {
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
index 169589f6d..d6d085d7f 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
@@ -1,5 +1,10 @@
#![warn(clippy::blocks_in_if_conditions)]
-#![allow(unused, clippy::let_and_return)]
+#![allow(
+ unused,
+ clippy::let_and_return,
+ clippy::needless_if,
+ clippy::unnecessary_literal_unwrap
+)]
fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool {
pfn(val)
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
index 941d604dd..5ac02e750 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
@@ -1,5 +1,5 @@
error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
- --> $DIR/blocks_in_if_conditions_closure.rs:18:17
+ --> $DIR/blocks_in_if_conditions_closure.rs:23:17
|
LL | |x| {
| _________________^
@@ -11,7 +11,7 @@ LL | | },
= note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
- --> $DIR/blocks_in_if_conditions_closure.rs:27:13
+ --> $DIR/blocks_in_if_conditions_closure.rs:32:13
|
LL | |x| {
| _____________^
diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed
index 670eef6a2..d6774c035 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_comparison.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
+#![allow(clippy::needless_if)]
#![warn(clippy::bool_comparison)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs
index 72851be63..c0483fd73 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_comparison.rs
@@ -1,5 +1,6 @@
//@run-rustfix
+#![allow(clippy::needless_if)]
#![warn(clippy::bool_comparison)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr
index 31522d4a5..f4dded365 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_comparison.stderr
@@ -1,5 +1,5 @@
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:7:8
+ --> $DIR/bool_comparison.rs:8:8
|
LL | if x == true {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
@@ -7,127 +7,127 @@ LL | if x == true {
= note: `-D clippy::bool-comparison` implied by `-D warnings`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:12:8
+ --> $DIR/bool_comparison.rs:13:8
|
LL | if x == false {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:17:8
+ --> $DIR/bool_comparison.rs:18:8
|
LL | if true == x {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:22:8
+ --> $DIR/bool_comparison.rs:23:8
|
LL | if false == x {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:27:8
+ --> $DIR/bool_comparison.rs:28:8
|
LL | if x != true {
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary
- --> $DIR/bool_comparison.rs:32:8
+ --> $DIR/bool_comparison.rs:33:8
|
LL | if x != false {
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: inequality checks against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:37:8
+ --> $DIR/bool_comparison.rs:38:8
|
LL | if true != x {
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary
- --> $DIR/bool_comparison.rs:42:8
+ --> $DIR/bool_comparison.rs:43:8
|
LL | if false != x {
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:47:8
+ --> $DIR/bool_comparison.rs:48:8
|
LL | if x < true {
| ^^^^^^^^ help: try simplifying it as shown: `!x`
error: greater than checks against false are unnecessary
- --> $DIR/bool_comparison.rs:52:8
+ --> $DIR/bool_comparison.rs:53:8
|
LL | if false < x {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: greater than checks against false are unnecessary
- --> $DIR/bool_comparison.rs:57:8
+ --> $DIR/bool_comparison.rs:58:8
|
LL | if x > false {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:62:8
+ --> $DIR/bool_comparison.rs:63:8
|
LL | if true > x {
| ^^^^^^^^ help: try simplifying it as shown: `!x`
error: order comparisons between booleans can be simplified
- --> $DIR/bool_comparison.rs:68:8
+ --> $DIR/bool_comparison.rs:69:8
|
LL | if x < y {
| ^^^^^ help: try simplifying it as shown: `!x & y`
error: order comparisons between booleans can be simplified
- --> $DIR/bool_comparison.rs:73:8
+ --> $DIR/bool_comparison.rs:74:8
|
LL | if x > y {
| ^^^^^ help: try simplifying it as shown: `x & !y`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:121:8
+ --> $DIR/bool_comparison.rs:122:8
|
LL | if a == !b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:122:8
+ --> $DIR/bool_comparison.rs:123:8
|
LL | if !a == b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:126:8
+ --> $DIR/bool_comparison.rs:127:8
|
LL | if b == !a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:127:8
+ --> $DIR/bool_comparison.rs:128:8
|
LL | if !b == a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:151:8
+ --> $DIR/bool_comparison.rs:152:8
|
LL | if false == m!(func) {}
| ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:152:8
+ --> $DIR/bool_comparison.rs:153:8
|
LL | if m!(func) == false {}
| ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:153:8
+ --> $DIR/bool_comparison.rs:154:8
|
LL | if true == m!(func) {}
| ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:154:8
+ --> $DIR/bool_comparison.rs:155:8
|
LL | if m!(func) == true {}
| ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
index 3f440ce00..996cc3650 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
@@ -1,9 +1,18 @@
//@run-rustfix
#![warn(clippy::borrow_as_ptr)]
+#![allow(clippy::useless_vec)]
+
+fn a() -> i32 {
+ 0
+}
fn main() {
let val = 1;
let _p = std::ptr::addr_of!(val);
+ let _p = &0 as *const i32;
+ let _p = &a() as *const i32;
+ let vec = vec![1];
+ let _p = &vec.len() as *const usize;
let mut val_mut = 1;
let _p_mut = std::ptr::addr_of_mut!(val_mut);
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
index c1ca9180e..5eafaeb2f 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
@@ -1,9 +1,18 @@
//@run-rustfix
#![warn(clippy::borrow_as_ptr)]
+#![allow(clippy::useless_vec)]
+
+fn a() -> i32 {
+ 0
+}
fn main() {
let val = 1;
let _p = &val as *const i32;
+ let _p = &0 as *const i32;
+ let _p = &a() as *const i32;
+ let vec = vec![1];
+ let _p = &vec.len() as *const usize;
let mut val_mut = 1;
let _p_mut = &mut val_mut as *mut i32;
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
index be1ed7330..c9990bb6f 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
@@ -1,5 +1,5 @@
error: borrow as raw pointer
- --> $DIR/borrow_as_ptr.rs:6:14
+ --> $DIR/borrow_as_ptr.rs:11:14
|
LL | let _p = &val as *const i32;
| ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)`
@@ -7,7 +7,7 @@ LL | let _p = &val as *const i32;
= note: `-D clippy::borrow-as-ptr` implied by `-D warnings`
error: borrow as raw pointer
- --> $DIR/borrow_as_ptr.rs:9:18
+ --> $DIR/borrow_as_ptr.rs:18:18
|
LL | let _p_mut = &mut val_mut as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
index 755264617..b951ba04c 100644
--- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(dead_code, unused_variables)]
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
index e319d365f..52980e55f 100644
--- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(dead_code, unused_variables)]
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs
index 29b08ab36..da940a4cf 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs
@@ -1,6 +1,6 @@
//@aux-build:helper.rs
-#![warn(clippy::borrow_interior_mutable_const)]
+#![deny(clippy::borrow_interior_mutable_const)]
#![allow(clippy::declare_interior_mutable_const)]
// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions.
@@ -19,7 +19,7 @@ const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
fn borrow_optional_cell() {
- let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability
+ let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability
let _ = &FROZEN_VARIANT;
}
@@ -34,11 +34,11 @@ trait AssocConsts {
// This is the "suboptimal behavior" mentioned in `is_value_unfrozen`
// caused by a similar reason to unfrozen types without any default values
// get linted even if it has frozen variants'.
- let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability
// The lint ignores default values because an impl of this trait can set
// an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`.
- let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability
}
}
@@ -47,9 +47,9 @@ impl AssocConsts for u64 {
const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
fn function() {
- let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT;
- let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability
let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
}
}
@@ -67,11 +67,11 @@ trait AssocTypes {
impl AssocTypes for u64 {
type ToBeUnfrozen = AtomicUsize;
- const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
fn function() {
- let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT;
}
}
@@ -83,19 +83,19 @@ enum BothOfCellAndGeneric<T> {
}
impl<T> BothOfCellAndGeneric<T> {
- const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
- const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
+ const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
fn function() {
- let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
- let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
+ let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability
+ let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability
let _ = &Self::FROZEN_VARIANT;
}
}
fn main() {
// constants defined in foreign crates
- let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+ let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT;
}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr
index b0cab977a..b753ec926 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr
@@ -1,16 +1,20 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:22:14
|
-LL | let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability
+LL | let _ = &UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
- = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+note: the lint level is defined here
+ --> $DIR/enums.rs:3:9
+ |
+LL | #![deny(clippy::borrow_interior_mutable_const)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:37:18
|
-LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+LL | let _ = &Self::TO_BE_FROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -18,7 +22,7 @@ LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:41:18
|
-LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -26,7 +30,7 @@ LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior muta
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:50:18
|
-LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -34,7 +38,7 @@ LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR i
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:52:18
|
-LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -42,7 +46,7 @@ LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mu
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:74:18
|
-LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -50,7 +54,7 @@ LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR in
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:91:18
|
-LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
+LL | let _ = &Self::UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -58,7 +62,7 @@ LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:92:18
|
-LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
+LL | let _ = &Self::GENERIC_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -66,7 +70,7 @@ LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/enums.rs:99:14
|
-LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
index 7c5786424..0ea93dd84 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::borrow_interior_mutable_const)]
+#![deny(clippy::borrow_interior_mutable_const)]
#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
#![allow(const_item_mutation)]
@@ -51,14 +51,14 @@ impl<T> std::ops::Deref for StaticRef<T> {
const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
fn main() {
- ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
- assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
+ ATOMIC.store(1, Ordering::SeqCst); //~ ERROR: interior mutability
+ assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR: interior mutability
let _once = ONCE_INIT;
- let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
- let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
- let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
- let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
+ let _once_ref = &ONCE_INIT; //~ ERROR: interior mutability
+ let _once_ref_2 = &&ONCE_INIT; //~ ERROR: interior mutability
+ let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR: interior mutability
+ let _once_mut = &mut ONCE_INIT; //~ ERROR: interior mutability
let _atomic_into_inner = ATOMIC.into_inner();
// these should be all fine.
let _twice = (ONCE_INIT, ONCE_INIT);
@@ -69,23 +69,23 @@ fn main() {
let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
// referencing projection is still bad.
- let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
- let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
- let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
- let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
- let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
+ let _ = &ATOMIC_TUPLE; //~ ERROR: interior mutability
+ let _ = &ATOMIC_TUPLE.0; //~ ERROR: interior mutability
+ let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR: interior mutability
+ let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR: interior mutability
+ let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR: interior mutability
let _ = &*ATOMIC_TUPLE.1;
let _ = &ATOMIC_TUPLE.2;
let _ = (&&&&ATOMIC_TUPLE).0;
let _ = (&&&&ATOMIC_TUPLE).2;
let _ = ATOMIC_TUPLE.0;
- let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+ let _ = ATOMIC_TUPLE.0[0]; //~ ERROR: interior mutability
let _ = ATOMIC_TUPLE.1.into_iter();
let _ = ATOMIC_TUPLE.2;
let _ = &{ ATOMIC_TUPLE };
- CELL.set(2); //~ ERROR interior mutability
- assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
+ CELL.set(2); //~ ERROR: interior mutability
+ assert_eq!(CELL.get(), 6); //~ ERROR: interior mutability
assert_eq!(INTEGER, 8);
assert!(STRING.is_empty());
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr
index c87ad206c..200e04b8f 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr
@@ -1,16 +1,20 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:54:5
|
-LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
+LL | ATOMIC.store(1, Ordering::SeqCst);
| ^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
- = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+note: the lint level is defined here
+ --> $DIR/others.rs:1:9
+ |
+LL | #![deny(clippy::borrow_interior_mutable_const)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:55:16
|
-LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
+LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
| ^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -18,7 +22,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:58:22
|
-LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
+LL | let _once_ref = &ONCE_INIT;
| ^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -26,7 +30,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:59:25
|
-LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
+LL | let _once_ref_2 = &&ONCE_INIT;
| ^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -34,7 +38,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:60:27
|
-LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
+LL | let _once_ref_4 = &&&&ONCE_INIT;
| ^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -42,7 +46,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:61:26
|
-LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
+LL | let _once_mut = &mut ONCE_INIT;
| ^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -50,7 +54,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:72:14
|
-LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
+LL | let _ = &ATOMIC_TUPLE;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -58,7 +62,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:73:14
|
-LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
+LL | let _ = &ATOMIC_TUPLE.0;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -66,7 +70,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:74:19
|
-LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
+LL | let _ = &(&&&&ATOMIC_TUPLE).0;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -74,7 +78,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:75:14
|
-LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+LL | let _ = &ATOMIC_TUPLE.0[0];
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -82,7 +86,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:76:13
|
-LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
+LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -90,7 +94,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:82:13
|
-LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+LL | let _ = ATOMIC_TUPLE.0[0];
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -98,7 +102,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:87:5
|
-LL | CELL.set(2); //~ ERROR interior mutability
+LL | CELL.set(2);
| ^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -106,7 +110,7 @@ LL | CELL.set(2); //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:88:16
|
-LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
+LL | assert_eq!(CELL.get(), 6);
| ^^^^
|
= help: assign this const to a local or static variable, and use the variable here
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs
index 06b5d62e8..4da3833cb 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::borrow_interior_mutable_const)]
+#![deny(clippy::borrow_interior_mutable_const)]
#![allow(clippy::declare_interior_mutable_const)]
// this file replicates its `declare` counterpart. Please see it for more discussions.
@@ -12,7 +12,7 @@ trait ConcreteTypes {
const STRING: String;
fn function() {
- let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::ATOMIC; //~ ERROR: interior mutability
let _ = &Self::STRING;
}
}
@@ -23,7 +23,7 @@ impl ConcreteTypes for u64 {
fn function() {
// Lint this again since implementers can choose not to borrow it.
- let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::ATOMIC; //~ ERROR: interior mutability
let _ = &Self::STRING;
}
}
@@ -48,7 +48,7 @@ impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> {
fn function() {
let _ = &Self::TO_REMAIN_GENERIC;
- let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+ let _ = &Self::TO_BE_CONCRETE; //~ ERROR: interior mutability
}
}
@@ -83,8 +83,8 @@ impl<T: ConstDefault> AssocTypes for Vec<T> {
fn function() {
let _ = &Self::TO_BE_FROZEN;
- let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
- let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+ let _ = &Self::TO_BE_UNFROZEN; //~ ERROR: interior mutability
+ let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR: interior mutability
let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM;
}
}
@@ -106,7 +106,7 @@ where
fn function() {
let _ = &Self::NOT_BOUNDED;
- let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ let _ = &Self::BOUNDED; //~ ERROR: interior mutability
}
}
@@ -119,7 +119,7 @@ where
fn function() {
let _ = &Self::NOT_BOUNDED;
- let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ let _ = &Self::BOUNDED; //~ ERROR: interior mutability
}
}
@@ -148,8 +148,8 @@ impl SelfType for AtomicUsize {
const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
fn function() {
- let _ = &Self::SELF; //~ ERROR interior mutable
- let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+ let _ = &Self::SELF; //~ ERROR: interior mutability
+ let _ = &Self::WRAPPED_SELF; //~ ERROR: interior mutability
}
}
@@ -159,7 +159,7 @@ trait BothOfCellAndGeneric<T> {
fn function() {
let _ = &Self::DIRECT;
- let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ let _ = &Self::INDIRECT; //~ ERROR: interior mutability
}
}
@@ -169,7 +169,7 @@ impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
fn function() {
let _ = &Self::DIRECT;
- let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ let _ = &Self::INDIRECT; //~ ERROR: interior mutability
}
}
@@ -188,15 +188,15 @@ where
const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
fn function() {
- let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::ATOMIC; //~ ERROR: interior mutability
let _ = &Self::COW;
let _ = &Self::GENERIC_TYPE;
let _ = &Self::ASSOC_TYPE;
- let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+ let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR: interior mutability
}
}
fn main() {
- u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
- assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
+ u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR: interior mutability
+ assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR: interior mutability
}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr
index f34ae8814..add223acd 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr
@@ -1,16 +1,20 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:15:18
|
-LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+LL | let _ = &Self::ATOMIC;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
- = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+note: the lint level is defined here
+ --> $DIR/traits.rs:1:9
+ |
+LL | #![deny(clippy::borrow_interior_mutable_const)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:26:18
|
-LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+LL | let _ = &Self::ATOMIC;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -18,7 +22,7 @@ LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:51:18
|
-LL | let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+LL | let _ = &Self::TO_BE_CONCRETE;
| ^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -26,7 +30,7 @@ LL | let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:86:18
|
-LL | let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
+LL | let _ = &Self::TO_BE_UNFROZEN;
| ^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -34,7 +38,7 @@ LL | let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:87:18
|
-LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -42,7 +46,7 @@ LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:109:18
|
-LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
+LL | let _ = &Self::BOUNDED;
| ^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -50,7 +54,7 @@ LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:122:18
|
-LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
+LL | let _ = &Self::BOUNDED;
| ^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -58,7 +62,7 @@ LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:151:18
|
-LL | let _ = &Self::SELF; //~ ERROR interior mutable
+LL | let _ = &Self::SELF;
| ^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -66,7 +70,7 @@ LL | let _ = &Self::SELF; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:152:18
|
-LL | let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+LL | let _ = &Self::WRAPPED_SELF;
| ^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -74,7 +78,7 @@ LL | let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:162:18
|
-LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
+LL | let _ = &Self::INDIRECT;
| ^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -82,7 +86,7 @@ LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:172:18
|
-LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
+LL | let _ = &Self::INDIRECT;
| ^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -90,7 +94,7 @@ LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:191:18
|
-LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+LL | let _ = &Self::ATOMIC;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -98,7 +102,7 @@ LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:195:18
|
-LL | let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+LL | let _ = &Self::BOUNDED_ASSOC_TYPE;
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -106,7 +110,7 @@ LL | let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:200:5
|
-LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
+LL | u64::ATOMIC.store(5, Ordering::SeqCst);
| ^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
@@ -114,7 +118,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
error: a `const` item with interior mutability should not be borrowed
--> $DIR/traits.rs:201:16
|
-LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
+LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
| ^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs
index 2d6055eb6..5780ea089 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs
@@ -1,6 +1,10 @@
#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
#![allow(dead_code)]
-#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)]
+#![allow(
+ clippy::mixed_read_write_in_expression,
+ clippy::uninlined_format_args,
+ clippy::needless_else
+)]
// This tests valid if blocks that shouldn't trigger the lint
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr
index ce7fff012..a7e72b780 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr
@@ -1,5 +1,5 @@
error: this `if` has identical blocks
- --> $DIR/valid_if_blocks.rs:105:14
+ --> $DIR/valid_if_blocks.rs:109:14
|
LL | if false {
| ______________^
@@ -7,7 +7,7 @@ LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/valid_if_blocks.rs:106:12
+ --> $DIR/valid_if_blocks.rs:110:12
|
LL | } else {
| ____________^
@@ -20,7 +20,7 @@ LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `if` has identical blocks
- --> $DIR/valid_if_blocks.rs:116:15
+ --> $DIR/valid_if_blocks.rs:120:15
|
LL | if x == 0 {
| _______________^
@@ -31,7 +31,7 @@ LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/valid_if_blocks.rs:120:12
+ --> $DIR/valid_if_blocks.rs:124:12
|
LL | } else {
| ____________^
@@ -42,19 +42,19 @@ LL | | }
| |_____^
error: this `if` has identical blocks
- --> $DIR/valid_if_blocks.rs:127:23
+ --> $DIR/valid_if_blocks.rs:131:23
|
LL | let _ = if x == 6 { 7 } else { 7 };
| ^^^^^
|
note: same as this
- --> $DIR/valid_if_blocks.rs:127:34
+ --> $DIR/valid_if_blocks.rs:131:34
|
LL | let _ = if x == 6 { 7 } else { 7 };
| ^^^^^
error: this `if` has identical blocks
- --> $DIR/valid_if_blocks.rs:133:23
+ --> $DIR/valid_if_blocks.rs:137:23
|
LL | } else if x == 68 {
| _______________________^
@@ -66,7 +66,7 @@ LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/valid_if_blocks.rs:138:12
+ --> $DIR/valid_if_blocks.rs:142:12
|
LL | } else {
| ____________^
@@ -78,7 +78,7 @@ LL | | };
| |_____^
error: this `if` has identical blocks
- --> $DIR/valid_if_blocks.rs:147:23
+ --> $DIR/valid_if_blocks.rs:151:23
|
LL | } else if x == 68 {
| _______________________^
@@ -88,7 +88,7 @@ LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/valid_if_blocks.rs:150:12
+ --> $DIR/valid_if_blocks.rs:154:12
|
LL | } else {
| ____________^
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.rs b/src/tools/clippy/tests/ui/builtin_type_shadow.rs
index 69b8b6a0e..c5addd534 100644
--- a/src/tools/clippy/tests/ui/builtin_type_shadow.rs
+++ b/src/tools/clippy/tests/ui/builtin_type_shadow.rs
@@ -3,7 +3,7 @@
fn foo<u32>(a: u32) -> u32 {
42
- // ^ rustc's type error
+ //~^ ERROR: mismatched types
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/bytecount.rs b/src/tools/clippy/tests/ui/bytecount.rs
index d3ad26921..4d168bfea 100644
--- a/src/tools/clippy/tests/ui/bytecount.rs
+++ b/src/tools/clippy/tests/ui/bytecount.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::needless_borrow)]
+#![allow(clippy::needless_borrow, clippy::useless_vec)]
#[deny(clippy::naive_bytecount)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index a86b85706..60a0eabf5 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -41,6 +41,14 @@ fn main() {
1u32 as i32;
1u64 as i64;
1usize as isize;
+ 1usize as i8; // should not wrap, usize is never 8 bits
+ 1usize as i16; // wraps on 16 bit ptr size
+ 1usize as i32; // wraps on 32 bit ptr size
+ 1usize as i64; // wraps on 64 bit ptr size
+ 1u8 as isize; // should not wrap, isize is never 8 bits
+ 1u16 as isize; // wraps on 16 bit ptr size
+ 1u32 as isize; // wraps on 32 bit ptr size
+ 1u64 as isize; // wraps on 64 bit ptr size
// Test clippy::cast_sign_loss
1i32 as u32;
-1i32 as u32;
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 65ecf1aa3..de29af78e 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -215,20 +215,110 @@ error: casting `usize` to `isize` may wrap around the value
LL | 1usize as isize;
| ^^^^^^^^^^^^^^^
-error: casting `i32` to `u32` may lose the sign of the value
+error: casting `usize` to `i8` may truncate the value
+ --> $DIR/cast.rs:44:5
+ |
+LL | 1usize as i8; // should not wrap, usize is never 8 bits
+ | ^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i8::try_from(1usize); // should not wrap, usize is never 8 bits
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: casting `usize` to `i16` may truncate the value
+ --> $DIR/cast.rs:45:5
+ |
+LL | 1usize as i16; // wraps on 16 bit ptr size
+ | ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i16::try_from(1usize); // wraps on 16 bit ptr size
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers
+ --> $DIR/cast.rs:45:5
+ |
+LL | 1usize as i16; // wraps on 16 bit ptr size
+ | ^^^^^^^^^^^^^
+ |
+ = note: `usize` and `isize` may be as small as 16 bits on some platforms
+ = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
+
+error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
--> $DIR/cast.rs:46:5
|
+LL | 1usize as i32; // wraps on 32 bit ptr size
+ | ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i32::try_from(1usize); // wraps on 32 bit ptr size
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast.rs:46:5
+ |
+LL | 1usize as i32; // wraps on 32 bit ptr size
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers
+ --> $DIR/cast.rs:47:5
+ |
+LL | 1usize as i64; // wraps on 64 bit ptr size
+ | ^^^^^^^^^^^^^
+
+error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers
+ --> $DIR/cast.rs:49:5
+ |
+LL | 1u16 as isize; // wraps on 16 bit ptr size
+ | ^^^^^^^^^^^^^
+ |
+ = note: `usize` and `isize` may be as small as 16 bits on some platforms
+ = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
+
+error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast.rs:50:5
+ |
+LL | 1u32 as isize; // wraps on 32 bit ptr size
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast.rs:51:5
+ |
+LL | 1u64 as isize; // wraps on 64 bit ptr size
+ | ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | isize::try_from(1u64); // wraps on 64 bit ptr size
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
+ --> $DIR/cast.rs:51:5
+ |
+LL | 1u64 as isize; // wraps on 64 bit ptr size
+ | ^^^^^^^^^^^^^
+
+error: casting `i32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:54:5
+ |
LL | -1i32 as u32;
| ^^^^^^^^^^^^
error: casting `isize` to `usize` may lose the sign of the value
- --> $DIR/cast.rs:48:5
+ --> $DIR/cast.rs:56:5
|
LL | -1isize as usize;
| ^^^^^^^^^^^^^^^^
error: casting `i64` to `i8` may truncate the value
- --> $DIR/cast.rs:115:5
+ --> $DIR/cast.rs:123:5
|
LL | (-99999999999i64).min(1) as i8; // should be linted because signed
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -240,7 +330,7 @@ LL | i8::try_from((-99999999999i64).min(1)); // should be linted because sig
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `u8` may truncate the value
- --> $DIR/cast.rs:127:5
+ --> $DIR/cast.rs:135:5
|
LL | 999999u64.clamp(0, 256) as u8; // should still be linted
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -252,7 +342,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E2` to `u8` may truncate the value
- --> $DIR/cast.rs:148:21
+ --> $DIR/cast.rs:156:21
|
LL | let _ = self as u8;
| ^^^^^^^^^^
@@ -264,7 +354,7 @@ LL | let _ = u8::try_from(self);
| ~~~~~~~~~~~~~~~~~~
error: casting `main::E2::B` to `u8` will truncate the value
- --> $DIR/cast.rs:149:21
+ --> $DIR/cast.rs:157:21
|
LL | let _ = Self::B as u8;
| ^^^^^^^^^^^^^
@@ -272,7 +362,7 @@ LL | let _ = Self::B as u8;
= note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
error: casting `main::E5` to `i8` may truncate the value
- --> $DIR/cast.rs:185:21
+ --> $DIR/cast.rs:193:21
|
LL | let _ = self as i8;
| ^^^^^^^^^^
@@ -284,13 +374,13 @@ LL | let _ = i8::try_from(self);
| ~~~~~~~~~~~~~~~~~~
error: casting `main::E5::A` to `i8` will truncate the value
- --> $DIR/cast.rs:186:21
+ --> $DIR/cast.rs:194:21
|
LL | let _ = Self::A as i8;
| ^^^^^^^^^^^^^
error: casting `main::E6` to `i16` may truncate the value
- --> $DIR/cast.rs:200:21
+ --> $DIR/cast.rs:208:21
|
LL | let _ = self as i16;
| ^^^^^^^^^^^
@@ -302,7 +392,7 @@ LL | let _ = i16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~
error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
- --> $DIR/cast.rs:215:21
+ --> $DIR/cast.rs:223:21
|
LL | let _ = self as usize;
| ^^^^^^^^^^^^^
@@ -314,7 +404,7 @@ LL | let _ = usize::try_from(self);
| ~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E10` to `u16` may truncate the value
- --> $DIR/cast.rs:256:21
+ --> $DIR/cast.rs:264:21
|
LL | let _ = self as u16;
| ^^^^^^^^^^^
@@ -326,7 +416,7 @@ LL | let _ = u16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:264:13
+ --> $DIR/cast.rs:272:13
|
LL | let c = (q >> 16) as u8;
| ^^^^^^^^^^^^^^^
@@ -338,7 +428,7 @@ LL | let c = u8::try_from(q >> 16);
| ~~~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:267:13
+ --> $DIR/cast.rs:275:13
|
LL | let c = (q / 1000) as u8;
| ^^^^^^^^^^^^^^^^
@@ -349,5 +439,5 @@ help: ... or use `try_from` and handle the error accordingly
LL | let c = u8::try_from(q / 1000);
| ~~~~~~~~~~~~~~~~~~~~~~
-error: aborting due to 41 previous errors
+error: aborting due to 51 previous errors
diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs
deleted file mode 100644
index c48a734ba..000000000
--- a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-#![warn(clippy::cast_ref_to_mut)]
-#![allow(clippy::no_effect, clippy::borrow_as_ptr)]
-
-extern "C" {
- // N.B., mutability can be easily incorrect in FFI calls -- as
- // in C, the default is mutable pointers.
- fn ffi(c: *mut u8);
- fn int_ffi(c: *mut i32);
-}
-
-fn main() {
- let s = String::from("Hello");
- let a = &s;
- unsafe {
- let num = &3i32;
- let mut_num = &mut 3i32;
- // Should be warned against
- (*(a as *const _ as *mut String)).push_str(" world");
- *(a as *const _ as *mut _) = String::from("Replaced");
- *(a as *const _ as *mut String) += " world";
- // Shouldn't be warned against
- println!("{}", *(num as *const _ as *const i16));
- println!("{}", *(mut_num as *mut _ as *mut i16));
- ffi(a.as_ptr() as *mut _);
- int_ffi(num as *const _ as *mut _);
- int_ffi(&3 as *const _ as *mut _);
- let mut value = 3;
- let value: *const i32 = &mut value;
- *(value as *const i16 as *mut i16) = 42;
- }
-}
diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr
deleted file mode 100644
index aacd99437..000000000
--- a/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
- --> $DIR/cast_ref_to_mut.rs:18:9
- |
-LL | (*(a as *const _ as *mut String)).push_str(" world");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings`
-
-error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
- --> $DIR/cast_ref_to_mut.rs:19:9
- |
-LL | *(a as *const _ as *mut _) = String::from("Replaced");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
- --> $DIR/cast_ref_to_mut.rs:20:9
- |
-LL | *(a as *const _ as *mut String) += " world";
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
index b77f01883..27e03ebb7 100644
--- a/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
+++ b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::unnecessary_cast)]
fn main() {
let x: [i32; 3] = [1_i32, 2, 3];
diff --git a/src/tools/clippy/tests/ui/cfg_features.rs b/src/tools/clippy/tests/ui/cfg_features.rs
new file mode 100644
index 000000000..bc4109c2c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_features.rs
@@ -0,0 +1,12 @@
+#![warn(clippy::maybe_misused_cfg)]
+
+fn main() {
+ #[cfg(features = "not-really-a-feature")]
+ let _ = 1 + 2;
+
+ #[cfg(all(feature = "right", features = "wrong"))]
+ let _ = 1 + 2;
+
+ #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))]
+ let _ = 1 + 2;
+}
diff --git a/src/tools/clippy/tests/ui/cfg_features.stderr b/src/tools/clippy/tests/ui/cfg_features.stderr
new file mode 100644
index 000000000..00405985d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_features.stderr
@@ -0,0 +1,28 @@
+error: feature may misspelled as features
+ --> $DIR/cfg_features.rs:4:11
+ |
+LL | #[cfg(features = "not-really-a-feature")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `feature = "not-really-a-feature"`
+ |
+ = note: `-D clippy::maybe-misused-cfg` implied by `-D warnings`
+
+error: feature may misspelled as features
+ --> $DIR/cfg_features.rs:7:34
+ |
+LL | #[cfg(all(feature = "right", features = "wrong"))]
+ | ^^^^^^^^^^^^^^^^^^ help: use: `feature = "wrong"`
+
+error: feature may misspelled as features
+ --> $DIR/cfg_features.rs:10:15
+ |
+LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))]
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `feature = "wrong1"`
+
+error: feature may misspelled as features
+ --> $DIR/cfg_features.rs:10:59
+ |
+LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))]
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `feature = "wrong2"`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
index ec082c73b..16e54a7d9 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
@@ -1,5 +1,9 @@
#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
-#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::branches_sharing_code,
+ clippy::unnecessary_literal_unwrap
+)]
fn test_complex_conditions() {
let x: Result<(), ()> = Ok(());
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
index d44d5072e..c395c5ba0 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
@@ -1,5 +1,5 @@
error: called `unwrap` on `x` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:8:9
+ --> $DIR/complex_conditionals.rs:12:9
|
LL | if x.is_ok() && y.is_err() {
| --------- the check is happening here
@@ -14,7 +14,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap_err()` will always panic
- --> $DIR/complex_conditionals.rs:9:9
+ --> $DIR/complex_conditionals.rs:13:9
|
LL | if x.is_ok() && y.is_err() {
| --------- because of this check
@@ -29,7 +29,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:10:9
+ --> $DIR/complex_conditionals.rs:14:9
|
LL | if x.is_ok() && y.is_err() {
| ---------- because of this check
@@ -38,7 +38,7 @@ LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `y` after checking its variant with `is_err`
- --> $DIR/complex_conditionals.rs:11:9
+ --> $DIR/complex_conditionals.rs:15:9
|
LL | if x.is_ok() && y.is_err() {
| ---------- the check is happening here
@@ -49,7 +49,7 @@ LL | y.unwrap_err(); // unnecessary
= help: try using `if let` or `match`
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:25:9
+ --> $DIR/complex_conditionals.rs:29:9
|
LL | if x.is_ok() || y.is_ok() {
| --------- because of this check
@@ -58,7 +58,7 @@ LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `x` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:26:9
+ --> $DIR/complex_conditionals.rs:30:9
|
LL | if x.is_ok() || y.is_ok() {
| --------- the check is happening here
@@ -69,7 +69,7 @@ LL | x.unwrap_err(); // unnecessary
= help: try using `if let` or `match`
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:27:9
+ --> $DIR/complex_conditionals.rs:31:9
|
LL | if x.is_ok() || y.is_ok() {
| --------- because of this check
@@ -78,7 +78,7 @@ LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `y` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:28:9
+ --> $DIR/complex_conditionals.rs:32:9
|
LL | if x.is_ok() || y.is_ok() {
| --------- the check is happening here
@@ -89,7 +89,7 @@ LL | y.unwrap_err(); // unnecessary
= help: try using `if let` or `match`
error: called `unwrap` on `x` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:32:9
+ --> $DIR/complex_conditionals.rs:36:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| --------- the check is happening here
@@ -99,7 +99,7 @@ LL | x.unwrap(); // unnecessary
= help: try using `if let` or `match`
error: this call to `unwrap_err()` will always panic
- --> $DIR/complex_conditionals.rs:33:9
+ --> $DIR/complex_conditionals.rs:37:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| --------- because of this check
@@ -108,7 +108,7 @@ LL | x.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:34:9
+ --> $DIR/complex_conditionals.rs:38:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| --------- because of this check
@@ -117,7 +117,7 @@ LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `y` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:35:9
+ --> $DIR/complex_conditionals.rs:39:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| --------- the check is happening here
@@ -128,7 +128,7 @@ LL | y.unwrap_err(); // unnecessary
= help: try using `if let` or `match`
error: called `unwrap` on `z` after checking its variant with `is_err`
- --> $DIR/complex_conditionals.rs:36:9
+ --> $DIR/complex_conditionals.rs:40:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| ---------- the check is happening here
@@ -139,7 +139,7 @@ LL | z.unwrap(); // unnecessary
= help: try using `if let` or `match`
error: this call to `unwrap_err()` will always panic
- --> $DIR/complex_conditionals.rs:37:9
+ --> $DIR/complex_conditionals.rs:41:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
| ---------- because of this check
@@ -148,7 +148,7 @@ LL | z.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:45:9
+ --> $DIR/complex_conditionals.rs:49:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| --------- because of this check
@@ -157,7 +157,7 @@ LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `x` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:46:9
+ --> $DIR/complex_conditionals.rs:50:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| --------- the check is happening here
@@ -168,7 +168,7 @@ LL | x.unwrap_err(); // unnecessary
= help: try using `if let` or `match`
error: called `unwrap` on `y` after checking its variant with `is_ok`
- --> $DIR/complex_conditionals.rs:47:9
+ --> $DIR/complex_conditionals.rs:51:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| --------- the check is happening here
@@ -179,7 +179,7 @@ LL | y.unwrap(); // unnecessary
= help: try using `if let` or `match`
error: this call to `unwrap_err()` will always panic
- --> $DIR/complex_conditionals.rs:48:9
+ --> $DIR/complex_conditionals.rs:52:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| --------- because of this check
@@ -188,7 +188,7 @@ LL | y.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals.rs:49:9
+ --> $DIR/complex_conditionals.rs:53:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| ---------- because of this check
@@ -197,7 +197,7 @@ LL | z.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `z` after checking its variant with `is_err`
- --> $DIR/complex_conditionals.rs:50:9
+ --> $DIR/complex_conditionals.rs:54:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
| ---------- the check is happening here
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
index 043ea4148..e417cf833 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
@@ -1,5 +1,9 @@
#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
-#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::branches_sharing_code,
+ clippy::unnecessary_literal_unwrap
+)]
fn test_nested() {
fn nested() {
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
index 542ab5330..049a69d93 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
@@ -1,5 +1,5 @@
error: called `unwrap` on `x` after checking its variant with `is_some`
- --> $DIR/complex_conditionals_nested.rs:8:13
+ --> $DIR/complex_conditionals_nested.rs:12:13
|
LL | if x.is_some() {
| -------------- help: try: `if let Some(..) = x`
@@ -13,7 +13,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/complex_conditionals_nested.rs:10:13
+ --> $DIR/complex_conditionals_nested.rs:14:13
|
LL | if x.is_some() {
| ----------- because of this check
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
index 82dce8197..61042bb90 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -1,6 +1,10 @@
#![feature(lint_reasons)]
#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
-#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::branches_sharing_code,
+ clippy::unnecessary_literal_unwrap
+)]
macro_rules! m {
($a:expr) => {
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index ef6882742..93809f655 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -1,5 +1,5 @@
error: called `unwrap` on `x` after checking its variant with `is_some`
- --> $DIR/simple_conditionals.rs:40:9
+ --> $DIR/simple_conditionals.rs:44:9
|
LL | if x.is_some() {
| -------------- help: try: `if let Some(..) = x`
@@ -13,7 +13,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: called `expect` on `x` after checking its variant with `is_some`
- --> $DIR/simple_conditionals.rs:41:9
+ --> $DIR/simple_conditionals.rs:45:9
|
LL | if x.is_some() {
| -------------- help: try: `if let Some(..) = x`
@@ -22,7 +22,7 @@ LL | x.expect("an error message"); // unnecessary
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/simple_conditionals.rs:43:9
+ --> $DIR/simple_conditionals.rs:47:9
|
LL | if x.is_some() {
| ----------- because of this check
@@ -37,7 +37,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `expect()` will always panic
- --> $DIR/simple_conditionals.rs:44:9
+ --> $DIR/simple_conditionals.rs:48:9
|
LL | if x.is_some() {
| ----------- because of this check
@@ -46,7 +46,7 @@ LL | x.expect("an error message"); // will panic
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/simple_conditionals.rs:47:9
+ --> $DIR/simple_conditionals.rs:51:9
|
LL | if x.is_none() {
| ----------- because of this check
@@ -54,7 +54,7 @@ LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap` on `x` after checking its variant with `is_none`
- --> $DIR/simple_conditionals.rs:49:9
+ --> $DIR/simple_conditionals.rs:53:9
|
LL | if x.is_none() {
| -------------- help: try: `if let Some(..) = x`
@@ -63,7 +63,7 @@ LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: called `unwrap` on `x` after checking its variant with `is_some`
- --> $DIR/simple_conditionals.rs:8:13
+ --> $DIR/simple_conditionals.rs:12:13
|
LL | if $a.is_some() {
| --------------- help: try: `if let Some(..) = x`
@@ -76,7 +76,7 @@ LL | m!(x);
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
error: called `unwrap` on `x` after checking its variant with `is_ok`
- --> $DIR/simple_conditionals.rs:57:9
+ --> $DIR/simple_conditionals.rs:61:9
|
LL | if x.is_ok() {
| ------------ help: try: `if let Ok(..) = x`
@@ -84,7 +84,7 @@ LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: called `expect` on `x` after checking its variant with `is_ok`
- --> $DIR/simple_conditionals.rs:58:9
+ --> $DIR/simple_conditionals.rs:62:9
|
LL | if x.is_ok() {
| ------------ help: try: `if let Ok(..) = x`
@@ -93,7 +93,7 @@ LL | x.expect("an error message"); // unnecessary
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call to `unwrap_err()` will always panic
- --> $DIR/simple_conditionals.rs:59:9
+ --> $DIR/simple_conditionals.rs:63:9
|
LL | if x.is_ok() {
| --------- because of this check
@@ -102,7 +102,7 @@ LL | x.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/simple_conditionals.rs:61:9
+ --> $DIR/simple_conditionals.rs:65:9
|
LL | if x.is_ok() {
| --------- because of this check
@@ -111,7 +111,7 @@ LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: this call to `expect()` will always panic
- --> $DIR/simple_conditionals.rs:62:9
+ --> $DIR/simple_conditionals.rs:66:9
|
LL | if x.is_ok() {
| --------- because of this check
@@ -120,7 +120,7 @@ LL | x.expect("an error message"); // will panic
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: called `unwrap_err` on `x` after checking its variant with `is_ok`
- --> $DIR/simple_conditionals.rs:63:9
+ --> $DIR/simple_conditionals.rs:67:9
|
LL | if x.is_ok() {
| ------------ help: try: `if let Err(..) = x`
@@ -129,7 +129,7 @@ LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: this call to `unwrap()` will always panic
- --> $DIR/simple_conditionals.rs:66:9
+ --> $DIR/simple_conditionals.rs:70:9
|
LL | if x.is_err() {
| ---------- because of this check
@@ -137,7 +137,7 @@ LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: called `unwrap_err` on `x` after checking its variant with `is_err`
- --> $DIR/simple_conditionals.rs:67:9
+ --> $DIR/simple_conditionals.rs:71:9
|
LL | if x.is_err() {
| ------------- help: try: `if let Err(..) = x`
@@ -146,7 +146,7 @@ LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: called `unwrap` on `x` after checking its variant with `is_err`
- --> $DIR/simple_conditionals.rs:69:9
+ --> $DIR/simple_conditionals.rs:73:9
|
LL | if x.is_err() {
| ------------- help: try: `if let Ok(..) = x`
@@ -155,7 +155,7 @@ LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: this call to `unwrap_err()` will always panic
- --> $DIR/simple_conditionals.rs:70:9
+ --> $DIR/simple_conditionals.rs:74:9
|
LL | if x.is_err() {
| ---------- because of this check
diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
index 8f9f2a0db..b7c186bef 100644
--- a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
+++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::incorrect_clone_impl_on_copy_type)]
+
use std::fmt;
use std::marker::PhantomData;
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
index b6e09ab31..34bd22334 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
@@ -2,6 +2,7 @@
#![warn(clippy::cloned_instead_of_copied)]
#![allow(unused)]
+#![allow(clippy::useless_vec)]
fn main() {
// yay
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
index fa9e1a996..fa8444937 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
@@ -2,6 +2,7 @@
#![warn(clippy::cloned_instead_of_copied)]
#![allow(unused)]
+#![allow(clippy::useless_vec)]
fn main() {
// yay
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
index e0361acd9..3ce482006 100644
--- a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
@@ -1,5 +1,5 @@
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:8:24
+ --> $DIR/cloned_instead_of_copied.rs:9:24
|
LL | let _ = [1].iter().cloned();
| ^^^^^^ help: try: `copied`
@@ -7,43 +7,43 @@ LL | let _ = [1].iter().cloned();
= note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:9:31
+ --> $DIR/cloned_instead_of_copied.rs:10:31
|
LL | let _ = vec!["hi"].iter().cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:10:22
+ --> $DIR/cloned_instead_of_copied.rs:11:22
|
LL | let _ = Some(&1).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:11:34
+ --> $DIR/cloned_instead_of_copied.rs:12:34
|
LL | let _ = Box::new([1].iter()).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:12:32
+ --> $DIR/cloned_instead_of_copied.rs:13:32
|
LL | let _ = Box::new(Some(&1)).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:28:22
+ --> $DIR/cloned_instead_of_copied.rs:29:22
|
LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:33:24
+ --> $DIR/cloned_instead_of_copied.rs:34:24
|
LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:34:22
+ --> $DIR/cloned_instead_of_copied.rs:35:22
|
LL | let _ = Some(&1).cloned();
| ^^^^^^ help: try: `copied`
diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs
deleted file mode 100644
index 64ca52b01..000000000
--- a/src/tools/clippy/tests/ui/cmp_nan.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-const NAN_F32: f32 = f32::NAN;
-const NAN_F64: f64 = f64::NAN;
-
-#[warn(clippy::cmp_nan)]
-#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)]
-fn main() {
- let x = 5f32;
- x == f32::NAN;
- x != f32::NAN;
- x < f32::NAN;
- x > f32::NAN;
- x <= f32::NAN;
- x >= f32::NAN;
- x == NAN_F32;
- x != NAN_F32;
- x < NAN_F32;
- x > NAN_F32;
- x <= NAN_F32;
- x >= NAN_F32;
-
- let y = 0f64;
- y == f64::NAN;
- y != f64::NAN;
- y < f64::NAN;
- y > f64::NAN;
- y <= f64::NAN;
- y >= f64::NAN;
- y == NAN_F64;
- y != NAN_F64;
- y < NAN_F64;
- y > NAN_F64;
- y <= NAN_F64;
- y >= NAN_F64;
-}
diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr
deleted file mode 100644
index 867516661..000000000
--- a/src/tools/clippy/tests/ui/cmp_nan.stderr
+++ /dev/null
@@ -1,148 +0,0 @@
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:8:5
- |
-LL | x == f32::NAN;
- | ^^^^^^^^^^^^^
- |
- = note: `-D clippy::cmp-nan` implied by `-D warnings`
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:9:5
- |
-LL | x != f32::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:10:5
- |
-LL | x < f32::NAN;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:11:5
- |
-LL | x > f32::NAN;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:12:5
- |
-LL | x <= f32::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:13:5
- |
-LL | x >= f32::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:14:5
- |
-LL | x == NAN_F32;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:15:5
- |
-LL | x != NAN_F32;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:16:5
- |
-LL | x < NAN_F32;
- | ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:17:5
- |
-LL | x > NAN_F32;
- | ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:18:5
- |
-LL | x <= NAN_F32;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:19:5
- |
-LL | x >= NAN_F32;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:22:5
- |
-LL | y == f64::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:23:5
- |
-LL | y != f64::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:24:5
- |
-LL | y < f64::NAN;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:25:5
- |
-LL | y > f64::NAN;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:26:5
- |
-LL | y <= f64::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:27:5
- |
-LL | y >= f64::NAN;
- | ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:28:5
- |
-LL | y == NAN_F64;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:29:5
- |
-LL | y != NAN_F64;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:30:5
- |
-LL | y < NAN_F64;
- | ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:31:5
- |
-LL | y > NAN_F64;
- | ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:32:5
- |
-LL | y <= NAN_F64;
- | ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
- --> $DIR/cmp_nan.rs:33:5
- |
-LL | y >= NAN_F64;
- | ^^^^^^^^^^^^
-
-error: aborting due to 24 previous errors
-
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
index 3bf3deb9b..118346348 100644
--- a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
@@ -1,5 +1,10 @@
//@run-rustfix
-#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700
+#![allow(
+ unused,
+ clippy::needless_if,
+ clippy::redundant_clone,
+ clippy::derive_partial_eq_without_eq
+)] // See #5700
// Define the types in each module to avoid trait impls leaking between modules.
macro_rules! impl_types {
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
index 10107dc8f..3a25d53a5 100644
--- a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
@@ -1,5 +1,10 @@
//@run-rustfix
-#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700
+#![allow(
+ unused,
+ clippy::needless_if,
+ clippy::redundant_clone,
+ clippy::derive_partial_eq_without_eq
+)] // See #5700
// Define the types in each module to avoid trait impls leaking between modules.
macro_rules! impl_types {
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
index 43bf8851f..4714a0daa 100644
--- a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
@@ -1,5 +1,5 @@
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:42:12
+ --> $DIR/asymmetric_partial_eq.rs:47:12
|
LL | if borrowed.to_owned() == owned {}
| ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
@@ -7,7 +7,7 @@ LL | if borrowed.to_owned() == owned {}
= note: `-D clippy::cmp-owned` implied by `-D warnings`
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:43:21
+ --> $DIR/asymmetric_partial_eq.rs:48:21
|
LL | if owned == borrowed.to_owned() {}
| ---------^^^^^^^^^^^^^^^^^^^
@@ -15,13 +15,13 @@ LL | if owned == borrowed.to_owned() {}
| help: try: `borrowed == owned`
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:61:21
+ --> $DIR/asymmetric_partial_eq.rs:66:21
|
LL | if owned == borrowed.to_owned() {}
| ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:62:12
+ --> $DIR/asymmetric_partial_eq.rs:67:12
|
LL | if borrowed.to_owned() == owned {}
| ^^^^^^^^^^^^^^^^^^^---------
@@ -29,7 +29,7 @@ LL | if borrowed.to_owned() == owned {}
| help: try: `owned == borrowed`
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:88:20
+ --> $DIR/asymmetric_partial_eq.rs:93:20
|
LL | if "Hi" == borrowed.to_string() {}
| --------^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL | if "Hi" == borrowed.to_string() {}
| help: try: `borrowed == "Hi"`
error: this creates an owned instance just for comparison
- --> $DIR/asymmetric_partial_eq.rs:89:12
+ --> $DIR/asymmetric_partial_eq.rs:94:12
|
LL | if borrowed.to_string() == "Hi" {}
| ^^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
index 76f90ab2a..bf1a58588 100644
--- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
+++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
@@ -31,7 +31,7 @@ struct Foo;
impl PartialEq for Foo {
// Allow this here, because it emits the lint
// without a suggestion. This is tested in
- // `tests/ui/cmp_owned/without_suggestion.rs`
+ // `$DIR/without_suggestion.rs`
#[allow(clippy::cmp_owned)]
fn eq(&self, other: &Self) -> bool {
self.to_owned() == *other
diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr
index 5824631fa..d86724630 100644
--- a/src/tools/clippy/tests/ui/cognitive_complexity.stderr
+++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr
@@ -40,6 +40,14 @@ LL | fn bar() {
= help: you could split it up into multiple smaller functions
error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:178:4
+ |
+LL | fn dont_warn_on_tests() {
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
--> $DIR/cognitive_complexity.rs:186:4
|
LL | fn barr() {
@@ -151,5 +159,5 @@ LL | pub async fn async_method() {
|
= help: you could split it up into multiple smaller functions
-error: aborting due to 19 previous errors
+error: aborting due to 20 previous errors
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
index 8302cec45..c4116cd85 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
#[rustfmt::skip]
#[warn(clippy::collapsible_if)]
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs
index 5913dcf41..8f51d0ee5 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.rs
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
#[rustfmt::skip]
#[warn(clippy::collapsible_if)]
diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed
index c6514a559..e305e1d7a 100644
--- a/src/tools/clippy/tests/ui/collapsible_if.fixed
+++ b/src/tools/clippy/tests/ui/collapsible_if.fixed
@@ -2,6 +2,7 @@
#![allow(
clippy::assertions_on_constants,
clippy::equatable_if_let,
+ clippy::needless_if,
clippy::nonminimal_bool,
clippy::eq_op
)]
diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs
index 2c85b68df..7c52959d3 100644
--- a/src/tools/clippy/tests/ui/collapsible_if.rs
+++ b/src/tools/clippy/tests/ui/collapsible_if.rs
@@ -2,6 +2,7 @@
#![allow(
clippy::assertions_on_constants,
clippy::equatable_if_let,
+ clippy::needless_if,
clippy::nonminimal_bool,
clippy::eq_op
)]
diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr
index c687bae1a..4a1a9e8a6 100644
--- a/src/tools/clippy/tests/ui/collapsible_if.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_if.stderr
@@ -1,5 +1,5 @@
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:14:5
+ --> $DIR/collapsible_if.rs:15:5
|
LL | / if x == "hello" {
LL | | if y == "world" {
@@ -17,7 +17,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:20:5
+ --> $DIR/collapsible_if.rs:21:5
|
LL | / if x == "hello" || x == "world" {
LL | | if y == "world" || y == "hello" {
@@ -34,7 +34,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:26:5
+ --> $DIR/collapsible_if.rs:27:5
|
LL | / if x == "hello" && x == "world" {
LL | | if y == "world" || y == "hello" {
@@ -51,7 +51,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:32:5
+ --> $DIR/collapsible_if.rs:33:5
|
LL | / if x == "hello" || x == "world" {
LL | | if y == "world" && y == "hello" {
@@ -68,7 +68,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:38:5
+ --> $DIR/collapsible_if.rs:39:5
|
LL | / if x == "hello" && x == "world" {
LL | | if y == "world" && y == "hello" {
@@ -85,7 +85,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:44:5
+ --> $DIR/collapsible_if.rs:45:5
|
LL | / if 42 == 1337 {
LL | | if 'a' != 'A' {
@@ -102,7 +102,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:100:5
+ --> $DIR/collapsible_if.rs:101:5
|
LL | / if x == "hello" {
LL | | if y == "world" { // Collapsible
@@ -119,7 +119,7 @@ LL + }
|
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:159:5
+ --> $DIR/collapsible_if.rs:160:5
|
LL | / if matches!(true, true) {
LL | | if matches!(true, true) {}
@@ -127,7 +127,7 @@ LL | | }
| |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
error: this `if` statement can be collapsed
- --> $DIR/collapsible_if.rs:164:5
+ --> $DIR/collapsible_if.rs:165:5
|
LL | / if matches!(true, true) && truth() {
LL | | if matches!(true, true) {}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
index 2c1a42a72..e02c1c572 100644
--- a/src/tools/clippy/tests/ui/collection_is_never_read.rs
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -1,4 +1,4 @@
-#![allow(unused)]
+#![allow(unused, clippy::useless_vec)]
#![warn(clippy::collection_is_never_read)]
use std::collections::{HashMap, HashSet};
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
index dd2615ab2..c92dd509e 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
@@ -1,6 +1,7 @@
//@run-rustfix
#![warn(clippy::comparison_to_empty)]
+#![allow(clippy::useless_vec)]
fn main() {
// Disallow comparisons to empty
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs
index 5462784c6..b34897143 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.rs
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs
@@ -1,6 +1,7 @@
//@run-rustfix
#![warn(clippy::comparison_to_empty)]
+#![allow(clippy::useless_vec)]
fn main() {
// Disallow comparisons to empty
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
index f69d6bd52..cc09b17eb 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
@@ -1,5 +1,5 @@
error: comparison to empty slice
- --> $DIR/comparison_to_empty.rs:8:13
+ --> $DIR/comparison_to_empty.rs:9:13
|
LL | let _ = s == "";
| ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
@@ -7,19 +7,19 @@ LL | let _ = s == "";
= note: `-D clippy::comparison-to-empty` implied by `-D warnings`
error: comparison to empty slice
- --> $DIR/comparison_to_empty.rs:9:13
+ --> $DIR/comparison_to_empty.rs:10:13
|
LL | let _ = s != "";
| ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()`
error: comparison to empty slice
- --> $DIR/comparison_to_empty.rs:12:13
+ --> $DIR/comparison_to_empty.rs:13:13
|
LL | let _ = v == [];
| ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()`
error: comparison to empty slice
- --> $DIR/comparison_to_empty.rs:13:13
+ --> $DIR/comparison_to_empty.rs:14:13
|
LL | let _ = v != [];
| ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()`
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
index 66419656a..5dffddc11 100644
--- a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
@@ -1,13 +1,5 @@
-//@compile-flags: --emit=link
-//@no-prefer-dynamic
-// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
-// crates. If we don't set this, compiletest will override the `crate_type` attribute below and
-// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't
-// contain a proc-macro.
-
#![feature(repr128)]
#![allow(incomplete_features)]
-#![crate_type = "proc-macro"]
extern crate proc_macro;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
index c7f022482..0df22f413 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-10148.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
@@ -1,4 +1,4 @@
-//@aux-build:../../auxiliary/proc_macros.rs
+//@aux-build:../auxiliary/proc_macros.rs:proc-macro
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10645.rs b/src/tools/clippy/tests/ui/crashes/ice-10645.rs
index 4d8698d38..6e126aff7 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-10645.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-10645.rs
@@ -1,4 +1,4 @@
-// compile-flags: --cap-lints=warn
+//@compile-flags: --cap-lints=warn
// https://github.com/rust-lang/rust-clippy/issues/10645
#![warn(clippy::future_not_send)]
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr b/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
index fc084e30d..0055fe061 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
@@ -1,4 +1,4 @@
-error: future cannot be sent between threads safely
+warning: future cannot be sent between threads safely
--> $DIR/ice-10645.rs:5:35
|
LL | pub async fn bar<'a, T: 'a>(_: T) {}
@@ -12,5 +12,5 @@ LL | pub async fn bar<'a, T: 'a>(_: T) {}
= note: `T` doesn't implement `std::marker::Send`
= note: `-D clippy::future-not-send` implied by `-D warnings`
-error: aborting due to previous error
+warning: 1 warning emitted
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10912.rs b/src/tools/clippy/tests/ui/crashes/ice-10912.rs
new file mode 100644
index 000000000..69d7f2f39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10912.rs
@@ -0,0 +1,4 @@
+#![warn(clippy::unreadable_literal)]
+fn f2() -> impl Sized { && 3.14159265358979323846E }
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10912.stderr b/src/tools/clippy/tests/ui/crashes/ice-10912.stderr
new file mode 100644
index 000000000..a74ce7315
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10912.stderr
@@ -0,0 +1,16 @@
+error: expected at least one digit in exponent
+ --> $DIR/ice-10912.rs:2:28
+ |
+LL | fn f2() -> impl Sized { && 3.14159265358979323846E }
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: long literal lacking separators
+ --> $DIR/ice-10912.rs:2:28
+ |
+LL | fn f2() -> impl Sized { && 3.14159265358979323846E }
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider: `3.141_592_653_589_793_238_46`
+ |
+ = note: `-D clippy::unreadable-literal` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11065.rs b/src/tools/clippy/tests/ui/crashes/ice-11065.rs
new file mode 100644
index 000000000..f5cf6b1cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11065.rs
@@ -0,0 +1,19 @@
+#![warn(clippy::useless_conversion)]
+
+use std::iter::FromIterator;
+use std::option::IntoIter as OptionIter;
+
+fn eq<T: Eq>(a: T, b: T) -> bool {
+ a == b
+}
+
+macro_rules! tests {
+ ($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
+ const C: $ty = $expr;
+ assert!(eq(C($($test),*), $expr($($test),*)));
+ })+})
+}
+
+tests! {
+ FromIterator::from_iter, fn(OptionIter<i32>) -> Vec<i32>, (Some(5).into_iter());
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs
index 81af88962..19ab03418 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-1782.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs
@@ -1,4 +1,5 @@
#![allow(dead_code, unused_variables)]
+#![allow(clippy::unnecessary_cast)]
/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`
///
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr
index c5ea0b16d..a166ccb3e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr
@@ -1,8 +1,8 @@
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/ice-2774.rs:15:1
+ --> $DIR/ice-2774.rs:15:28
|
LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
help: elide the lifetimes
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
index b40205288..21cd9d337 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3462.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
@@ -1,5 +1,5 @@
#![warn(clippy::all)]
-#![allow(clippy::disallowed_names, clippy::equatable_if_let)]
+#![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)]
#![allow(unused)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3741.rs b/src/tools/clippy/tests/ui/crashes/ice-3741.rs
index 3106a2e72..268c5ba0a 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3741.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3741.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_crash.rs
+//@aux-build:proc_macro_crash.rs:proc-macro
#![warn(clippy::suspicious_else_formatting)]
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4968.rs b/src/tools/clippy/tests/ui/crashes/ice-4968.rs
index ac724ac93..504738680 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-4968.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-4968.rs
@@ -1,5 +1,3 @@
-//@check-pass
-
// Test for https://github.com/rust-lang/rust-clippy/issues/4968
#![warn(clippy::unsound_collection_transmute)]
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.rs b/src/tools/clippy/tests/ui/crashes/ice-5497.rs
index 0769bce5f..f77f691c1 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-5497.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-5497.rs
@@ -7,5 +7,5 @@ pub trait Foo {
impl<T: Foo> Foo for Vec<T> {
const OOB: i32 = [1][1] + T::OOB;
- //~^ ERROR operation will panic
+ //~^ ERROR: operation will panic
}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5579.rs b/src/tools/clippy/tests/ui/crashes/ice-5579.rs
index e1842c73f..8ab36bbf9 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-5579.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-5579.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::unnecessary_literal_unwrap)]
+
trait IsErr {
fn is_err(&self, err: &str) -> bool;
}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
index 4506d1550..97390af3e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
@@ -1,9 +1,3 @@
-error[E0601]: `main` function not found in crate `ice_6250`
- --> $DIR/ice-6250.rs:16:2
- |
-LL | }
- | ^ consider adding a `main` function to `$DIR/ice-6250.rs`
-
error[E0308]: mismatched types
--> $DIR/ice-6250.rs:12:14
|
@@ -12,11 +6,6 @@ LL | for reference in vec![1, 2, 3] {
...
LL | Some(reference) = cache.data.get(key) {
| ^^^^^^^^^ expected integer, found `&i32`
- |
-help: consider dereferencing the borrow
- |
-LL | Some(*reference) = cache.data.get(key) {
- | +
error[E0308]: mismatched types
--> $DIR/ice-6250.rs:12:9
@@ -29,7 +18,6 @@ help: consider adding `let`
LL | let Some(reference) = cache.data.get(key) {
| +++
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0308, E0601.
-For more information about an error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr
index 8da2965c6..68a5766c9 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr
@@ -1,9 +1,3 @@
-error[E0601]: `main` function not found in crate `ice_6251`
- --> $DIR/ice-6251.rs:6:2
- |
-LL | }
- | ^ consider adding a `main` function to `$DIR/ice-6251.rs`
-
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/ice-6251.rs:4:45
|
@@ -35,7 +29,7 @@ LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
= note: expected type `usize`
found closure `[closure@$DIR/ice-6251.rs:4:44: 4:53]`
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0277, E0308, E0601.
+Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.rs b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
index ccde6aa2b..b6555ac5c 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6255.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
@@ -4,7 +4,6 @@
macro_rules! define_other_core {
( ) => {
extern crate std as core;
- //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern`
};
}
@@ -13,3 +12,4 @@ fn main() {
}
define_other_core!();
+//~^ ERROR: macro-expanded `extern crate` items cannot shadow names passed with `--extern`
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.rs b/src/tools/clippy/tests/ui/crashes/ice-6256.rs
index f9ee3e058..1d336b3cd 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6256.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6256.rs
@@ -10,6 +10,6 @@ impl dyn TT {
#[rustfmt::skip]
fn main() {
- let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
- //[nll]~^ ERROR: borrowed data escapes outside of closure
+ let f = |x: &dyn TT| x.func();
+ //~^ ERROR: borrowed data escapes outside of closure
}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr
index 9cfcccf1e..671933157 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr
@@ -1,7 +1,7 @@
error[E0521]: borrowed data escapes outside of closure
--> $DIR/ice-6256.rs:13:26
|
-LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+LL | let f = |x: &dyn TT| x.func();
| - - ^^^^^^^^
| | | |
| | | `x` escapes the closure body here
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.rs b/src/tools/clippy/tests/ui/crashes/ice-7169.rs
index 82095febc..b203252f0 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-7169.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-7169.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::needless_if)]
+
#[derive(Default)]
struct A<T> {
a: Vec<A<T>>,
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.stderr b/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
index 5a9cd3238..84e0af3f0 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/ice-7169.rs:8:12
+ --> $DIR/ice-7169.rs:10:12
|
LL | if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
| -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::<String>::default()).is_ok()`
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
index ffe20ab1c..a2683b3ce 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-7410.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
@@ -1,11 +1,12 @@
//@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-macos
-//@ignore-windows
+//@ignore-target-apple
+//@ignore-target-windows
#![feature(lang_items, start, libc)]
#![no_std]
#![allow(clippy::if_same_then_else)]
#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::needless_else)]
use core::panic::PanicInfo;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr
new file mode 100644
index 000000000..a59d098e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr
@@ -0,0 +1,12 @@
+error: a `const` item should never be interior mutable
+ --> $DIR/ice-9445.rs:1:1
+ |
+LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+ |
+ = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.rs b/src/tools/clippy/tests/ui/crashes/ice-96721.rs
index 4b3fb7640..ca68ba3d0 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-96721.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.rs
@@ -4,7 +4,7 @@ macro_rules! foo {
};
}
-#[path = foo!()] //~ ERROR malformed `path` attribute
+#[path = foo!()] //~ ERROR: malformed `path` attribute
mod abc {}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
index 78c567b8e..712bd14c6 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
@@ -1,7 +1,7 @@
error: malformed `path` attribute input
--> $DIR/ice-96721.rs:7:1
|
-LL | #[path = foo!()] //~ ERROR malformed `path` attribute
+LL | #[path = foo!()]
| ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
index a238e7896..92821b6ec 100644
--- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
+++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
@@ -1,4 +1,4 @@
-#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
+#![deny(clippy::mut_mut, clippy::zero_ptr)]
#![allow(dead_code)]
// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need
@@ -8,13 +8,12 @@
// extern crate lazy_static;
// use std::collections::HashMap;
-/// ensure that we don't suggest `is_nan` and `is_null` inside constants
+/// ensure that we don't suggest `is_null` inside constants
/// FIXME: once const fn is stable, suggest these functions again in constants
const BAA: *const i32 = 0 as *const i32;
static mut BAR: *const i32 = BAA;
static mut FOO: *const i32 = 0 as *const i32;
-static mut BUH: bool = 42.0 < f32::NAN;
#[allow(unused_variables, unused_mut)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
index 0b0e0ad26..37484f5eb 100644
--- a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
+++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
@@ -1,8 +1,8 @@
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes_impl_trait.rs:15:5
+ --> $DIR/needless_lifetimes_impl_trait.rs:15:12
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
note: the lint level is defined here
--> $DIR/needless_lifetimes_impl_trait.rs:1:9
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
index d6cd594d7..aa76688d8 100644
--- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
+++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
@@ -1,4 +1,4 @@
-//@ignore-macos
+//@ignore-target-apple
#![feature(rustc_attrs)]
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr
deleted file mode 100644
index 3d79a115c..000000000
--- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: recursing into entrypoint `a`
- --> $DIR/entrypoint_recursion.rs:10:5
- |
-LL | a();
- | ^
- |
- = help: consider using another function for this recursion
- = note: `-D clippy::main-recursion` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
index a382135bb..32eba9695 100644
--- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
@@ -1,5 +1,5 @@
//@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-macos
+//@ignore-target-apple
#![feature(lang_items, start, libc)]
#![no_std]
diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs
index 10788d404..6c63c0989 100644
--- a/src/tools/clippy/tests/ui/dbg_macro.rs
+++ b/src/tools/clippy/tests/ui/dbg_macro.rs
@@ -1,4 +1,3 @@
-//@compile-flags: --test
#![warn(clippy::dbg_macro)]
fn foo(n: u32) -> u32 {
diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr
index 530e76633..3d2926259 100644
--- a/src/tools/clippy/tests/ui/dbg_macro.stderr
+++ b/src/tools/clippy/tests/ui/dbg_macro.stderr
@@ -1,5 +1,5 @@
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:5:22
+ --> $DIR/dbg_macro.rs:4:22
|
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n }
| ~~~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:10:8
+ --> $DIR/dbg_macro.rs:9:8
|
LL | if dbg!(n <= 1) {
| ^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | if n <= 1 {
| ~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:11:9
+ --> $DIR/dbg_macro.rs:10:9
|
LL | dbg!(1)
| ^^^^^^^
@@ -33,7 +33,7 @@ LL | 1
|
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:13:9
+ --> $DIR/dbg_macro.rs:12:9
|
LL | dbg!(n * factorial(n - 1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL | n * factorial(n - 1)
|
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:18:5
+ --> $DIR/dbg_macro.rs:17:5
|
LL | dbg!(42);
| ^^^^^^^^
@@ -55,7 +55,7 @@ LL | 42;
| ~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:19:5
+ --> $DIR/dbg_macro.rs:18:5
|
LL | dbg!(dbg!(dbg!(42)));
| ^^^^^^^^^^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL | dbg!(dbg!(42));
| ~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:20:14
+ --> $DIR/dbg_macro.rs:19:14
|
LL | foo(3) + dbg!(factorial(4));
| ^^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL | foo(3) + factorial(4);
| ~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:21:5
+ --> $DIR/dbg_macro.rs:20:5
|
LL | dbg!(1, 2, dbg!(3, 4));
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | (1, 2, dbg!(3, 4));
| ~~~~~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:22:5
+ --> $DIR/dbg_macro.rs:21:5
|
LL | dbg!(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^^^
@@ -99,7 +99,7 @@ LL | (1, 2, 3, 4, 5);
| ~~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:42:5
+ --> $DIR/dbg_macro.rs:41:5
|
LL | dbg!();
| ^^^^^^^
@@ -111,7 +111,7 @@ LL +
|
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:44:13
+ --> $DIR/dbg_macro.rs:43:13
|
LL | let _ = dbg!();
| ^^^^^^
@@ -122,7 +122,7 @@ LL | let _ = ();
| ~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:45:9
+ --> $DIR/dbg_macro.rs:44:9
|
LL | bar(dbg!());
| ^^^^^^
@@ -133,7 +133,7 @@ LL | bar(());
| ~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:46:10
+ --> $DIR/dbg_macro.rs:45:10
|
LL | foo!(dbg!());
| ^^^^^^
@@ -144,7 +144,7 @@ LL | foo!(());
| ~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:47:16
+ --> $DIR/dbg_macro.rs:46:16
|
LL | foo2!(foo!(dbg!()));
| ^^^^^^
@@ -155,7 +155,7 @@ LL | foo2!(foo!(()));
| ~~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:68:9
+ --> $DIR/dbg_macro.rs:67:9
|
LL | dbg!(2);
| ^^^^^^^
@@ -166,7 +166,7 @@ LL | 2;
| ~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:74:5
+ --> $DIR/dbg_macro.rs:73:5
|
LL | dbg!(1);
| ^^^^^^^
@@ -177,7 +177,7 @@ LL | 1;
| ~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:79:5
+ --> $DIR/dbg_macro.rs:78:5
|
LL | dbg!(1);
| ^^^^^^^
@@ -188,7 +188,7 @@ LL | 1;
| ~
error: the `dbg!` macro is intended as a debugging tool
- --> $DIR/dbg_macro.rs:85:9
+ --> $DIR/dbg_macro.rs:84:9
|
LL | dbg!(1);
| ^^^^^^^
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs
index f44518694..a88bf7b21 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs
@@ -9,7 +9,7 @@ enum OptionalCell {
}
// a constant with enums should be linted only when the used variant is unfrozen (#3962).
-const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR: interior mutable
const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
const fn unfrozen_variant() -> OptionalCell {
@@ -20,7 +20,7 @@ const fn frozen_variant() -> OptionalCell {
OptionalCell::Frozen
}
-const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR: interior mutable
const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
enum NestedInnermost {
@@ -43,10 +43,11 @@ struct NestedOutermost {
// a constant with enums should be linted according to its value, no matter how structs involve.
const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
+ //~^ ERROR: interior mutable
outer: NestedOuter::NestedInner(NestedInner {
inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
}),
-}; //~ ERROR interior mutable
+};
const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
outer: NestedOuter::NestedInner(NestedInner {
inner: NestedInnermost::Frozen,
@@ -56,11 +57,11 @@ const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
trait AssocConsts {
// When there's no default value, lint it only according to its type.
// Further details are on the corresponding code (`NonCopyConst::check_trait_item`).
- const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
- const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+ const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR: interior mutable
+ const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR: interior mutable
// Lint default values accordingly.
- const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+ const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR: interior mutable
const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
}
@@ -86,7 +87,7 @@ trait AssocTypes {
impl AssocTypes for u64 {
type ToBeUnfrozen = AtomicUsize;
- const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR: interior mutable
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
}
@@ -98,25 +99,25 @@ enum BothOfCellAndGeneric<T> {
}
impl<T> BothOfCellAndGeneric<T> {
- const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR: interior mutable
// This is a false positive. The argument about this is on `is_value_unfrozen_raw`
- const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR: interior mutable
const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
// This is what is likely to be a false negative when one tries to fix
// the `GENERIC_VARIANT` false positive.
- const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+ const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR: interior mutable
}
// associated types here is basically the same as the one above.
trait BothOfCellAndGenericWithAssocType {
type AssocType;
- const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
- BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
- const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = //~ ERROR: interior mutable
+ BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
+ const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR: interior mutable
const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5);
}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr
index 84198d546..6070df749 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr
@@ -1,7 +1,7 @@
error: a `const` item should never be interior mutable
--> $DIR/enums.rs:12:1
|
-LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| make this a static item (maybe with lazy_static)
@@ -11,7 +11,7 @@ LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(tru
error: a `const` item should never be interior mutable
--> $DIR/enums.rs:23:1
|
-LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| make this a static item (maybe with lazy_static)
@@ -24,65 +24,66 @@ LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
| |
| _make this a static item (maybe with lazy_static)
| |
+LL | |
LL | | outer: NestedOuter::NestedInner(NestedInner {
LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
LL | | }),
-LL | | }; //~ ERROR interior mutable
+LL | | };
| |__^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:59:5
+ --> $DIR/enums.rs:60:5
|
-LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:60:5
+ --> $DIR/enums.rs:61:5
|
-LL | const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+LL | const TO_BE_FROZEN_VARIANT: OptionalCell;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:63:5
+ --> $DIR/enums.rs:64:5
|
-LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:89:5
+ --> $DIR/enums.rs:90:5
|
-LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:101:5
+ --> $DIR/enums.rs:102:5
|
-LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut...
+LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:104:5
+ --> $DIR/enums.rs:105:5
|
-LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:110:5
+ --> $DIR/enums.rs:111:5
|
-LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:117:5
+ --> $DIR/enums.rs:118:5
|
LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
-LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
| |____________________________________________________________________^
error: a `const` item should never be interior mutable
- --> $DIR/enums.rs:119:5
+ --> $DIR/enums.rs:120:5
|
-LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu...
+LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
index 896596b56..1cec29806 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
@@ -6,17 +6,17 @@ use std::fmt::Display;
use std::sync::atomic::AtomicUsize;
use std::sync::Once;
-const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
-const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR: interior mutable
+const CELL: Cell<usize> = Cell::new(6); //~ ERROR: interior mutable
const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-//~^ ERROR interior mutable
+//~^ ERROR: interior mutable
macro_rules! declare_const {
($name:ident: $ty:ty = $e:expr) => {
const $name: $ty = $e;
};
}
-declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+declare_const!(_ONCE: Once = Once::new()); //~ ERROR: interior mutable
// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
@@ -24,12 +24,12 @@ const INTEGER: u8 = 8;
const STRING: String = String::new();
const STR: &str = "012345";
const COW: Cow<str> = Cow::Borrowed("abcdef");
-//^ note: a const item of Cow is used in the `postgres` package.
+// note: a const item of Cow is used in the `postgres` package.
const NO_ANN: &dyn Display = &70;
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
-//^ there should be no lints on this line
+// there should be no lints on the line above line
mod issue_8493 {
use std::cell::Cell;
@@ -40,7 +40,7 @@ mod issue_8493 {
macro_rules! issue_8493 {
() => {
- const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
+ const _BAZ: Cell<usize> = Cell::new(0);
static _FOOBAR: () = {
thread_local! {
static _VAR: Cell<i32> = const { Cell::new(0) };
@@ -49,7 +49,7 @@ mod issue_8493 {
};
}
- issue_8493!();
+ issue_8493!(); //~ ERROR: interior mutable
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
index 1fd6d7322..0259f6a4a 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
@@ -1,7 +1,7 @@
error: a `const` item should never be interior mutable
--> $DIR/others.rs:9:1
|
-LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
+LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| make this a static item (maybe with lazy_static)
@@ -11,7 +11,7 @@ LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
error: a `const` item should never be interior mutable
--> $DIR/others.rs:10:1
|
-LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+LL | const CELL: Cell<usize> = Cell::new(6);
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| make this a static item (maybe with lazy_static)
@@ -30,7 +30,7 @@ error: a `const` item should never be interior mutable
LL | const $name: $ty = $e;
| ^^^^^^^^^^^^^^^^^^^^^^
...
-LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+LL | declare_const!(_ONCE: Once = Once::new());
| ----------------------------------------- in this macro invocation
|
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -38,7 +38,7 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
error: a `const` item should never be interior mutable
--> $DIR/others.rs:43:13
|
-LL | const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
+LL | const _BAZ: Cell<usize> = Cell::new(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | issue_8493!();
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs
index 256a336db..a6ccdd270 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs
@@ -12,10 +12,10 @@ macro_rules! declare_const {
// a constant whose type is a concrete type should be linted at the definition site.
trait ConcreteTypes {
- const ATOMIC: AtomicUsize; //~ ERROR interior mutable
+ const ATOMIC: AtomicUsize; //~ ERROR: interior mutable
const INTEGER: u64;
const STRING: String;
- declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+ declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR: interior mutable
}
impl ConcreteTypes for u64 {
@@ -40,7 +40,7 @@ trait GenericTypes<T, U> {
impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
const TO_REMAIN_GENERIC: T = T::DEFAULT;
- const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+ const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR: interior mutable
}
// a helper type used below
@@ -65,8 +65,8 @@ impl<T: ConstDefault> AssocTypes for Vec<T> {
type ToBeGenericParam = T;
const TO_BE_FROZEN: Self::ToBeFrozen = 12;
- const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
- const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+ const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR: interior mutable
+ const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR: interior mutable
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
}
@@ -85,7 +85,7 @@ where
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
{
const NOT_BOUNDED: T::NotToBeBounded;
- const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+ const BOUNDED: T::ToBeBounded; //~ ERROR: interior mutable
}
impl<T> AssocTypesFromGenericParam<T> for u64
@@ -113,8 +113,8 @@ impl SelfType for u64 {
impl SelfType for AtomicUsize {
// this (interior mutable `Self` const) exists in `parking_lot`.
// `const_trait_impl` will replace it in the future, hopefully.
- const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
- const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+ const SELF: Self = AtomicUsize::new(17); //~ ERROR: interior mutable
+ const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR: interior mutable
}
// Even though a constant contains a generic type, if it also have an interior mutable type,
@@ -122,7 +122,7 @@ impl SelfType for AtomicUsize {
trait BothOfCellAndGeneric<T> {
// this is a false negative in the current implementation.
const DIRECT: Cell<T>;
- const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+ const INDIRECT: Cell<*const T>; //~ ERROR: interior mutable
}
impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
@@ -138,13 +138,13 @@ impl<T> Local<T>
where
T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
{
- const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+ const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR: interior mutable
const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
const GENERIC_TYPE: T = T::DEFAULT;
const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
- const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+ const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR: interior mutable
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr
index 7debe059f..ef62919df 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr
@@ -1,7 +1,7 @@
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:15:5
|
-LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable
+LL | const ATOMIC: AtomicUsize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
@@ -12,7 +12,7 @@ error: a `const` item should never be interior mutable
LL | const $name: $ty = $e;
| ^^^^^^^^^^^^^^^^^^^^^^
...
-LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC);
| ---------------------------------------------------------- in this macro invocation
|
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -20,55 +20,55 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR i
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:43:5
|
-LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:68:5
|
-LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:69:5
|
-LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:88:5
|
-LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+LL | const BOUNDED: T::ToBeBounded;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:116:5
|
-LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+LL | const SELF: Self = AtomicUsize::new(17);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:117:5
|
-LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:125:5
|
-LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+LL | const INDIRECT: Cell<*const T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:141:5
|
-LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/traits.rs:147:5
|
-LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 11 previous errors
diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs
index f7819068a..da0816830 100644
--- a/src/tools/clippy/tests/ui/def_id_nocore.rs
+++ b/src/tools/clippy/tests/ui/def_id_nocore.rs
@@ -1,4 +1,4 @@
-//@ignore-macos
+//@ignore-target-apple
#![feature(no_core, lang_items, start)]
#![no_core]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
index 9520efe63..02eb78060 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::default_numeric_fallback)]
#![allow(
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
index cacbdb4a9..79a966983 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::default_numeric_fallback)]
#![allow(
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
index fbabb8bcf..23272d07e 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(lint_reasons)]
#![warn(clippy::default_numeric_fallback)]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
index 7bfc390e4..fb1491416 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(lint_reasons)]
#![warn(clippy::default_numeric_fallback)]
diff --git a/src/tools/clippy/tests/ui/default_trait_access.fixed b/src/tools/clippy/tests/ui/default_trait_access.fixed
index bf5dca976..14eb6d572 100644
--- a/src/tools/clippy/tests/ui/default_trait_access.fixed
+++ b/src/tools/clippy/tests/ui/default_trait_access.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![deny(clippy::default_trait_access)]
#![allow(dead_code, unused_imports)]
#![allow(clippy::uninlined_format_args)]
diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs
index 5e8e9ce85..aa2ced0a7 100644
--- a/src/tools/clippy/tests/ui/default_trait_access.rs
+++ b/src/tools/clippy/tests/ui/default_trait_access.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![deny(clippy::default_trait_access)]
#![allow(dead_code, unused_imports)]
#![allow(clippy::uninlined_format_args)]
diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed
index b27d3bc10..0ecca1b8f 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.fixed
+++ b/src/tools/clippy/tests/ui/deref_addrof.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
-#![allow(clippy::return_self_not_must_use)]
+#![allow(clippy::return_self_not_must_use, clippy::useless_vec)]
#![warn(clippy::deref_addrof)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs
index 825090c7c..9f91310e6 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.rs
+++ b/src/tools/clippy/tests/ui/deref_addrof.rs
@@ -1,7 +1,7 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
-#![allow(clippy::return_self_not_must_use)]
+#![allow(clippy::return_self_not_must_use, clippy::useless_vec)]
#![warn(clippy::deref_addrof)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/deref_addrof_macro.rs b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
index c7e60f365..ce4b94a73 100644
--- a/src/tools/clippy/tests/ui/deref_addrof_macro.rs
+++ b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::deref_addrof)]
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index aa0efb85c..a10f3d010 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -268,4 +268,25 @@ impl Default for OtherGenericType {
}
}
+mod issue10158 {
+ pub trait T {}
+
+ #[derive(Default)]
+ pub struct S {}
+ impl T for S {}
+
+ pub struct Outer {
+ pub inner: Box<dyn T>,
+ }
+
+ impl Default for Outer {
+ fn default() -> Self {
+ Outer {
+ // Box::<S>::default() adjusts to Box<dyn T>
+ inner: Box::<S>::default(),
+ }
+ }
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 8dc999ad5..18cef1c5b 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -304,4 +304,25 @@ impl Default for OtherGenericType {
}
}
+mod issue10158 {
+ pub trait T {}
+
+ #[derive(Default)]
+ pub struct S {}
+ impl T for S {}
+
+ pub struct Outer {
+ pub inner: Box<dyn T>,
+ }
+
+ impl Default for Outer {
+ fn default() -> Self {
+ Outer {
+ // Box::<S>::default() adjusts to Box<dyn T>
+ inner: Box::<S>::default(),
+ }
+ }
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs
index 6e0ce55f5..e01079bc9 100644
--- a/src/tools/clippy/tests/ui/derive.rs
+++ b/src/tools/clippy/tests/ui/derive.rs
@@ -1,4 +1,4 @@
-#![allow(dead_code)]
+#![allow(clippy::incorrect_clone_impl_on_copy_type, dead_code)]
#![warn(clippy::expl_impl_clone_on_copy)]
diff --git a/src/tools/clippy/tests/ui/disallowed_names.rs b/src/tools/clippy/tests/ui/disallowed_names.rs
index e937c49f3..5889f0443 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui/disallowed_names.rs
@@ -1,5 +1,6 @@
#![allow(
dead_code,
+ clippy::needless_if,
clippy::similar_names,
clippy::single_match,
clippy::toplevel_ref_arg,
diff --git a/src/tools/clippy/tests/ui/disallowed_names.stderr b/src/tools/clippy/tests/ui/disallowed_names.stderr
index 78cb55096..9ab68b641 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.stderr
+++ b/src/tools/clippy/tests/ui/disallowed_names.stderr
@@ -1,5 +1,5 @@
error: use of a disallowed/placeholder name `foo`
- --> $DIR/disallowed_names.rs:11:9
+ --> $DIR/disallowed_names.rs:12:9
|
LL | fn test(foo: ()) {}
| ^^^
@@ -7,79 +7,79 @@ LL | fn test(foo: ()) {}
= note: `-D clippy::disallowed-names` implied by `-D warnings`
error: use of a disallowed/placeholder name `foo`
- --> $DIR/disallowed_names.rs:14:9
+ --> $DIR/disallowed_names.rs:15:9
|
LL | let foo = 42;
| ^^^
error: use of a disallowed/placeholder name `baz`
- --> $DIR/disallowed_names.rs:15:9
+ --> $DIR/disallowed_names.rs:16:9
|
LL | let baz = 42;
| ^^^
error: use of a disallowed/placeholder name `quux`
- --> $DIR/disallowed_names.rs:16:9
+ --> $DIR/disallowed_names.rs:17:9
|
LL | let quux = 42;
| ^^^^
error: use of a disallowed/placeholder name `foo`
- --> $DIR/disallowed_names.rs:27:10
+ --> $DIR/disallowed_names.rs:28:10
|
LL | (foo, Some(baz), quux @ Some(_)) => (),
| ^^^
error: use of a disallowed/placeholder name `baz`
- --> $DIR/disallowed_names.rs:27:20
+ --> $DIR/disallowed_names.rs:28:20
|
LL | (foo, Some(baz), quux @ Some(_)) => (),
| ^^^
error: use of a disallowed/placeholder name `quux`
- --> $DIR/disallowed_names.rs:27:26
+ --> $DIR/disallowed_names.rs:28:26
|
LL | (foo, Some(baz), quux @ Some(_)) => (),
| ^^^^
error: use of a disallowed/placeholder name `foo`
- --> $DIR/disallowed_names.rs:32:19
+ --> $DIR/disallowed_names.rs:33:19
|
LL | fn issue_1647(mut foo: u8) {
| ^^^
error: use of a disallowed/placeholder name `baz`
- --> $DIR/disallowed_names.rs:33:13
+ --> $DIR/disallowed_names.rs:34:13
|
LL | let mut baz = 0;
| ^^^
error: use of a disallowed/placeholder name `quux`
- --> $DIR/disallowed_names.rs:34:21
+ --> $DIR/disallowed_names.rs:35:21
|
LL | if let Some(mut quux) = Some(42) {}
| ^^^^
error: use of a disallowed/placeholder name `baz`
- --> $DIR/disallowed_names.rs:38:13
+ --> $DIR/disallowed_names.rs:39:13
|
LL | let ref baz = 0;
| ^^^
error: use of a disallowed/placeholder name `quux`
- --> $DIR/disallowed_names.rs:39:21
+ --> $DIR/disallowed_names.rs:40:21
|
LL | if let Some(ref quux) = Some(42) {}
| ^^^^
error: use of a disallowed/placeholder name `baz`
- --> $DIR/disallowed_names.rs:43:17
+ --> $DIR/disallowed_names.rs:44:17
|
LL | let ref mut baz = 0;
| ^^^
error: use of a disallowed/placeholder name `quux`
- --> $DIR/disallowed_names.rs:44:25
+ --> $DIR/disallowed_names.rs:45:25
|
LL | if let Some(ref mut quux) = Some(42) {}
| ^^^^
diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
index e8f992e6d..9b1619baf 100644
--- a/src/tools/clippy/tests/ui/diverging_sub_expression.rs
+++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
@@ -1,5 +1,6 @@
#![warn(clippy::diverging_sub_expression)]
#![allow(clippy::match_same_arms, clippy::overly_complex_bool_expr)]
+#![allow(clippy::nonminimal_bool)]
#[allow(clippy::empty_loop)]
fn diverge() -> ! {
loop {}
@@ -21,6 +22,7 @@ fn main() {
}
#[allow(dead_code, unused_variables)]
+#[rustfmt::skip]
fn foobar() {
loop {
let x = match 5 {
@@ -35,6 +37,20 @@ fn foobar() {
99 => return,
_ => true || panic!("boo"),
},
+ // lint blocks as well
+ 15 => true || { return; },
+ 16 => false || { return; },
+ // ... and when it's a single expression
+ 17 => true || { return },
+ 18 => false || { return },
+ // ... but not when there's both an expression and a statement
+ 19 => true || { _ = 1; return },
+ 20 => false || { _ = 1; return },
+ // ... or multiple statements
+ 21 => true || { _ = 1; return; },
+ 22 => false || { _ = 1; return; },
+ 23 => true || { return; true },
+ 24 => true || { return; true },
_ => true || break,
};
}
diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr
index 51a3b0d97..243a5cf53 100644
--- a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr
+++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr
@@ -1,5 +1,5 @@
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:19:10
+ --> $DIR/diverging_sub_expression.rs:20:10
|
LL | b || diverge();
| ^^^^^^^^^
@@ -7,34 +7,66 @@ LL | b || diverge();
= note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:20:10
+ --> $DIR/diverging_sub_expression.rs:21:10
|
LL | b || A.foo();
| ^^^^^^^
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:29:26
+ --> $DIR/diverging_sub_expression.rs:31:26
|
LL | 6 => true || return,
| ^^^^^^
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:30:26
+ --> $DIR/diverging_sub_expression.rs:32:26
|
LL | 7 => true || continue,
| ^^^^^^^^
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:33:26
+ --> $DIR/diverging_sub_expression.rs:35:26
|
LL | 3 => true || diverge(),
| ^^^^^^^^^
error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:38:26
+ --> $DIR/diverging_sub_expression.rs:38:30
+ |
+LL | _ => true || panic!("boo"),
+ | ^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:41:29
+ |
+LL | 15 => true || { return; },
+ | ^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:42:30
+ |
+LL | 16 => false || { return; },
+ | ^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:44:29
+ |
+LL | 17 => true || { return },
+ | ^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:45:30
+ |
+LL | 18 => false || { return },
+ | ^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:54:26
|
LL | _ => true || break,
| ^^^^^
-error: aborting due to 6 previous errors
+error: aborting due to 11 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 d3aa2816c..14444df4c 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -60,6 +60,7 @@ fn test_units() {
/// GitHub GitLab
/// IPv4 IPv6
/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// WebAssembly
/// NaN NaNs
/// OAuth GraphQL
/// OCaml
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index d1e7d8017..542d33b13 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -60,6 +60,7 @@ fn test_units() {
/// GitHub GitLab
/// IPv4 IPv6
/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// WebAssembly
/// NaN NaNs
/// OAuth GraphQL
/// OCaml
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 6c67c903c..94ef43afc 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -132,7 +132,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:74:5
+ --> $DIR/doc-fixable.rs:75:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -143,7 +143,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:91:5
+ --> $DIR/doc-fixable.rs:92:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:99:8
+ --> $DIR/doc-fixable.rs:100:8
|
LL | /// ## CamelCaseThing
| ^^^^^^^^^^^^^^
@@ -165,7 +165,7 @@ LL | /// ## `CamelCaseThing`
| ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:102:7
+ --> $DIR/doc-fixable.rs:103:7
|
LL | /// # CamelCaseThing
| ^^^^^^^^^^^^^^
@@ -176,7 +176,7 @@ LL | /// # `CamelCaseThing`
| ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:104:22
+ --> $DIR/doc-fixable.rs:105:22
|
LL | /// Not a title #897 CamelCaseThing
| ^^^^^^^^^^^^^^
@@ -187,7 +187,7 @@ LL | /// Not a title #897 `CamelCaseThing`
| ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:105:5
+ --> $DIR/doc-fixable.rs:106:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -198,7 +198,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:112:5
+ --> $DIR/doc-fixable.rs:113:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -209,7 +209,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:125:5
+ --> $DIR/doc-fixable.rs:126:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -220,7 +220,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:136:43
+ --> $DIR/doc-fixable.rs:137:43
|
LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^
@@ -231,7 +231,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
| ~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:141:5
+ --> $DIR/doc-fixable.rs:142:5
|
LL | And BarQuz too.
| ^^^^^^
@@ -242,7 +242,7 @@ LL | And `BarQuz` too.
| ~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:142:1
+ --> $DIR/doc-fixable.rs:143:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -253,7 +253,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
|
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:147:43
+ --> $DIR/doc-fixable.rs:148:43
|
LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^
@@ -264,7 +264,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
| ~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:152:5
+ --> $DIR/doc-fixable.rs:153:5
|
LL | And BarQuz too.
| ^^^^^^
@@ -275,7 +275,7 @@ LL | And `BarQuz` too.
| ~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:153:1
+ --> $DIR/doc-fixable.rs:154:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -286,7 +286,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
|
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:164:5
+ --> $DIR/doc-fixable.rs:165:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -297,7 +297,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
- --> $DIR/doc-fixable.rs:183:22
+ --> $DIR/doc-fixable.rs:184:22
|
LL | /// An iterator over mycrate::Collection's values.
| ^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs b/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs
new file mode 100644
index 000000000..f1a2c0575
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/needless_doctest_main.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::needless_doctest_main)]
+//! issue 10491:
+//! ```rust,no_test
+//! use std::collections::HashMap;
+//!
+//! fn main() {
+//! let mut m = HashMap::new();
+//! m.insert(1u32, 2u32);
+//! }
+//! ```
+
+/// some description here
+/// ```rust,no_test
+/// fn main() {
+/// foo()
+/// }
+/// ```
+fn foo() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs
index 0c8eac5cc..d21b046f1 100644
--- a/src/tools/clippy/tests/ui/doc_unsafe.rs
+++ b/src/tools/clippy/tests/ui/doc_unsafe.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(clippy::let_unit_value)]
diff --git a/src/tools/clippy/tests/ui/double_comparison.fixed b/src/tools/clippy/tests/ui/double_comparison.fixed
index c80ff671a..f8ca92ef0 100644
--- a/src/tools/clippy/tests/ui/double_comparison.fixed
+++ b/src/tools/clippy/tests/ui/double_comparison.fixed
@@ -1,4 +1,5 @@
//@run-rustfix
+#![allow(clippy::needless_if)]
fn main() {
let x = 1;
diff --git a/src/tools/clippy/tests/ui/double_comparison.rs b/src/tools/clippy/tests/ui/double_comparison.rs
index bc78694aa..47ff87bea 100644
--- a/src/tools/clippy/tests/ui/double_comparison.rs
+++ b/src/tools/clippy/tests/ui/double_comparison.rs
@@ -1,4 +1,5 @@
//@run-rustfix
+#![allow(clippy::needless_if)]
fn main() {
let x = 1;
diff --git a/src/tools/clippy/tests/ui/double_comparison.stderr b/src/tools/clippy/tests/ui/double_comparison.stderr
index 05ef4e25f..4df1c28ac 100644
--- a/src/tools/clippy/tests/ui/double_comparison.stderr
+++ b/src/tools/clippy/tests/ui/double_comparison.stderr
@@ -1,5 +1,5 @@
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:6:8
+ --> $DIR/double_comparison.rs:7:8
|
LL | if x == y || x < y {
| ^^^^^^^^^^^^^^^ help: try: `x <= y`
@@ -7,43 +7,43 @@ LL | if x == y || x < y {
= note: `-D clippy::double-comparisons` implied by `-D warnings`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:9:8
+ --> $DIR/double_comparison.rs:10:8
|
LL | if x < y || x == y {
| ^^^^^^^^^^^^^^^ help: try: `x <= y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:12:8
+ --> $DIR/double_comparison.rs:13:8
|
LL | if x == y || x > y {
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:15:8
+ --> $DIR/double_comparison.rs:16:8
|
LL | if x > y || x == y {
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:18:8
+ --> $DIR/double_comparison.rs:19:8
|
LL | if x < y || x > y {
| ^^^^^^^^^^^^^^ help: try: `x != y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:21:8
+ --> $DIR/double_comparison.rs:22:8
|
LL | if x > y || x < y {
| ^^^^^^^^^^^^^^ help: try: `x != y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:24:8
+ --> $DIR/double_comparison.rs:25:8
|
LL | if x <= y && x >= y {
| ^^^^^^^^^^^^^^^^ help: try: `x == y`
error: this binary expression can be simplified
- --> $DIR/double_comparison.rs:27:8
+ --> $DIR/double_comparison.rs:28:8
|
LL | if x >= y && x <= y {
| ^^^^^^^^^^^^^^^^ help: try: `x == y`
diff --git a/src/tools/clippy/tests/ui/drain_collect.fixed b/src/tools/clippy/tests/ui/drain_collect.fixed
new file mode 100644
index 000000000..11001bd31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drain_collect.fixed
@@ -0,0 +1,77 @@
+//@run-rustfix
+
+#![deny(clippy::drain_collect)]
+#![allow(dead_code)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn binaryheap(b: &mut BinaryHeap<i32>) -> BinaryHeap<i32> {
+ std::mem::take(b)
+}
+
+fn binaryheap_dont_lint(b: &mut BinaryHeap<i32>) -> HashSet<i32> {
+ b.drain().collect()
+}
+
+fn hashmap(b: &mut HashMap<i32, i32>) -> HashMap<i32, i32> {
+ std::mem::take(b)
+}
+
+fn hashmap_dont_lint(b: &mut HashMap<i32, i32>) -> Vec<(i32, i32)> {
+ b.drain().collect()
+}
+
+fn hashset(b: &mut HashSet<i32>) -> HashSet<i32> {
+ std::mem::take(b)
+}
+
+fn hashset_dont_lint(b: &mut HashSet<i32>) -> Vec<i32> {
+ b.drain().collect()
+}
+
+fn vecdeque(b: &mut VecDeque<i32>) -> VecDeque<i32> {
+ std::mem::take(b)
+}
+
+fn vecdeque_dont_lint(b: &mut VecDeque<i32>) -> HashSet<i32> {
+ b.drain(..).collect()
+}
+
+fn vec(b: &mut Vec<i32>) -> Vec<i32> {
+ std::mem::take(b)
+}
+
+fn vec2(b: &mut Vec<i32>) -> Vec<i32> {
+ std::mem::take(b)
+}
+
+fn vec3(b: &mut Vec<i32>) -> Vec<i32> {
+ std::mem::take(b)
+}
+
+fn vec4(b: &mut Vec<i32>) -> Vec<i32> {
+ std::mem::take(b)
+}
+
+fn vec_no_reborrow() -> Vec<i32> {
+ let mut b = vec![1, 2, 3];
+ std::mem::take(&mut b)
+}
+
+fn vec_dont_lint(b: &mut Vec<i32>) -> HashSet<i32> {
+ b.drain(..).collect()
+}
+
+fn string(b: &mut String) -> String {
+ std::mem::take(b)
+}
+
+fn string_dont_lint(b: &mut String) -> HashSet<char> {
+ b.drain(..).collect()
+}
+
+fn not_whole_length(v: &mut Vec<i32>) -> Vec<i32> {
+ v.drain(1..).collect()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/drain_collect.rs b/src/tools/clippy/tests/ui/drain_collect.rs
new file mode 100644
index 000000000..373a3ca35
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drain_collect.rs
@@ -0,0 +1,77 @@
+//@run-rustfix
+
+#![deny(clippy::drain_collect)]
+#![allow(dead_code)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn binaryheap(b: &mut BinaryHeap<i32>) -> BinaryHeap<i32> {
+ b.drain().collect()
+}
+
+fn binaryheap_dont_lint(b: &mut BinaryHeap<i32>) -> HashSet<i32> {
+ b.drain().collect()
+}
+
+fn hashmap(b: &mut HashMap<i32, i32>) -> HashMap<i32, i32> {
+ b.drain().collect()
+}
+
+fn hashmap_dont_lint(b: &mut HashMap<i32, i32>) -> Vec<(i32, i32)> {
+ b.drain().collect()
+}
+
+fn hashset(b: &mut HashSet<i32>) -> HashSet<i32> {
+ b.drain().collect()
+}
+
+fn hashset_dont_lint(b: &mut HashSet<i32>) -> Vec<i32> {
+ b.drain().collect()
+}
+
+fn vecdeque(b: &mut VecDeque<i32>) -> VecDeque<i32> {
+ b.drain(..).collect()
+}
+
+fn vecdeque_dont_lint(b: &mut VecDeque<i32>) -> HashSet<i32> {
+ b.drain(..).collect()
+}
+
+fn vec(b: &mut Vec<i32>) -> Vec<i32> {
+ b.drain(..).collect()
+}
+
+fn vec2(b: &mut Vec<i32>) -> Vec<i32> {
+ b.drain(0..).collect()
+}
+
+fn vec3(b: &mut Vec<i32>) -> Vec<i32> {
+ b.drain(..b.len()).collect()
+}
+
+fn vec4(b: &mut Vec<i32>) -> Vec<i32> {
+ b.drain(0..b.len()).collect()
+}
+
+fn vec_no_reborrow() -> Vec<i32> {
+ let mut b = vec![1, 2, 3];
+ b.drain(..).collect()
+}
+
+fn vec_dont_lint(b: &mut Vec<i32>) -> HashSet<i32> {
+ b.drain(..).collect()
+}
+
+fn string(b: &mut String) -> String {
+ b.drain(..).collect()
+}
+
+fn string_dont_lint(b: &mut String) -> HashSet<char> {
+ b.drain(..).collect()
+}
+
+fn not_whole_length(v: &mut Vec<i32>) -> Vec<i32> {
+ v.drain(1..).collect()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/drain_collect.stderr b/src/tools/clippy/tests/ui/drain_collect.stderr
new file mode 100644
index 000000000..0792f0254
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drain_collect.stderr
@@ -0,0 +1,68 @@
+error: you seem to be trying to move all elements into a new `BinaryHeap`
+ --> $DIR/drain_collect.rs:9:5
+ |
+LL | b.drain().collect()
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+ |
+note: the lint level is defined here
+ --> $DIR/drain_collect.rs:3:9
+ |
+LL | #![deny(clippy::drain_collect)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: you seem to be trying to move all elements into a new `HashMap`
+ --> $DIR/drain_collect.rs:17:5
+ |
+LL | b.drain().collect()
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `HashSet`
+ --> $DIR/drain_collect.rs:25:5
+ |
+LL | b.drain().collect()
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:33:5
+ |
+LL | b.drain(..).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:41:5
+ |
+LL | b.drain(..).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:45:5
+ |
+LL | b.drain(0..).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:49:5
+ |
+LL | b.drain(..b.len()).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:53:5
+ |
+LL | b.drain(0..b.len()).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: you seem to be trying to move all elements into a new `Vec`
+ --> $DIR/drain_collect.rs:58:5
+ |
+LL | b.drain(..).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
+
+error: you seem to be trying to move all elements into a new `String`
+ --> $DIR/drain_collect.rs:66:5
+ |
+LL | b.drain(..).collect()
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/else_if_without_else.rs b/src/tools/clippy/tests/ui/else_if_without_else.rs
index 879b3ac39..eb5e22665 100644
--- a/src/tools/clippy/tests/ui/else_if_without_else.rs
+++ b/src/tools/clippy/tests/ui/else_if_without_else.rs
@@ -43,7 +43,7 @@ fn main() {
if bla1() {
println!("if");
} else if bla2() {
- //~ ERROR else if without else
+ //~^ ERROR: `if` expression with an `else if`, but without a final `else`
println!("else if");
}
@@ -52,7 +52,7 @@ fn main() {
} else if bla2() {
println!("else if 1");
} else if bla3() {
- //~ ERROR else if without else
+ //~^ ERROR: `if` expression with an `else if`, but without a final `else`
println!("else if 2");
}
}
diff --git a/src/tools/clippy/tests/ui/else_if_without_else.stderr b/src/tools/clippy/tests/ui/else_if_without_else.stderr
index 90ccfb4fa..11baf7544 100644
--- a/src/tools/clippy/tests/ui/else_if_without_else.stderr
+++ b/src/tools/clippy/tests/ui/else_if_without_else.stderr
@@ -3,7 +3,7 @@ error: `if` expression with an `else if`, but without a final `else`
|
LL | } else if bla2() {
| ____________^
-LL | | //~ ERROR else if without else
+LL | |
LL | | println!("else if");
LL | | }
| |_____^
@@ -16,7 +16,7 @@ error: `if` expression with an `else if`, but without a final `else`
|
LL | } else if bla3() {
| ____________^
-LL | | //~ ERROR else if without else
+LL | |
LL | | println!("else if 2");
LL | | }
| |_____^
diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs
index e843770f5..cc36ce5f4 100644
--- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs
+++ b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs
@@ -1,4 +1,7 @@
-//@aux-build:proc_macro_attr.rs
+//@aux-build:proc_macro_attr.rs:proc-macro
+// Flaky test, see https://github.com/rust-lang/rust/issues/113585.
+//@ignore-32bit
+//@ignore-64bit
#![warn(clippy::empty_line_after_doc_comments)]
#![allow(clippy::assertions_on_constants)]
#![feature(custom_inner_attributes)]
diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
index 269e66ea0..bc54e0fd2 100644
--- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
+++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
@@ -1,4 +1,7 @@
-//@aux-build:proc_macro_attr.rs
+//@aux-build:proc_macro_attr.rs:proc-macro
+// Flaky test, see https://github.com/rust-lang/rust/issues/113585.
+//@ignore-32bit
+//@ignore-64bit
#![warn(clippy::empty_line_after_outer_attr)]
#![allow(clippy::assertions_on_constants)]
#![feature(custom_inner_attributes)]
diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs
index 54e8fb490..f1a55415c 100644
--- a/src/tools/clippy/tests/ui/empty_loop.rs
+++ b/src/tools/clippy/tests/ui/empty_loop.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::empty_loop)]
diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
index d564b2d24..f9ab443df 100644
--- a/src/tools/clippy/tests/ui/empty_loop_no_std.rs
+++ b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
@@ -1,5 +1,5 @@
//@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-macos
+//@ignore-target-apple
#![warn(clippy::empty_loop)]
#![feature(lang_items, start, libc)]
diff --git a/src/tools/clippy/tests/ui/endian_bytes.rs b/src/tools/clippy/tests/ui/endian_bytes.rs
new file mode 100644
index 000000000..6bf014fc8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/endian_bytes.rs
@@ -0,0 +1,127 @@
+#![allow(unused)]
+#![allow(clippy::diverging_sub_expression)]
+#![no_main]
+
+macro_rules! fn_body {
+ () => {
+ 2u8.to_ne_bytes();
+ 2i8.to_ne_bytes();
+ 2u16.to_ne_bytes();
+ 2i16.to_ne_bytes();
+ 2u32.to_ne_bytes();
+ 2i32.to_ne_bytes();
+ 2u64.to_ne_bytes();
+ 2i64.to_ne_bytes();
+ 2u128.to_ne_bytes();
+ 2i128.to_ne_bytes();
+ 2.0f32.to_ne_bytes();
+ 2.0f64.to_ne_bytes();
+ 2usize.to_ne_bytes();
+ 2isize.to_ne_bytes();
+ u8::from_ne_bytes(todo!());
+ i8::from_ne_bytes(todo!());
+ u16::from_ne_bytes(todo!());
+ i16::from_ne_bytes(todo!());
+ u32::from_ne_bytes(todo!());
+ i32::from_ne_bytes(todo!());
+ u64::from_ne_bytes(todo!());
+ i64::from_ne_bytes(todo!());
+ u128::from_ne_bytes(todo!());
+ i128::from_ne_bytes(todo!());
+ usize::from_ne_bytes(todo!());
+ isize::from_ne_bytes(todo!());
+ f32::from_ne_bytes(todo!());
+ f64::from_ne_bytes(todo!());
+
+ 2u8.to_le_bytes();
+ 2i8.to_le_bytes();
+ 2u16.to_le_bytes();
+ 2i16.to_le_bytes();
+ 2u32.to_le_bytes();
+ 2i32.to_le_bytes();
+ 2u64.to_le_bytes();
+ 2i64.to_le_bytes();
+ 2u128.to_le_bytes();
+ 2i128.to_le_bytes();
+ 2.0f32.to_le_bytes();
+ 2.0f64.to_le_bytes();
+ 2usize.to_le_bytes();
+ 2isize.to_le_bytes();
+ u8::from_le_bytes(todo!());
+ i8::from_le_bytes(todo!());
+ u16::from_le_bytes(todo!());
+ i16::from_le_bytes(todo!());
+ u32::from_le_bytes(todo!());
+ i32::from_le_bytes(todo!());
+ u64::from_le_bytes(todo!());
+ i64::from_le_bytes(todo!());
+ u128::from_le_bytes(todo!());
+ i128::from_le_bytes(todo!());
+ usize::from_le_bytes(todo!());
+ isize::from_le_bytes(todo!());
+ f32::from_le_bytes(todo!());
+ f64::from_le_bytes(todo!());
+ };
+}
+
+// bless breaks if I use fn_body too much (oops)
+macro_rules! fn_body_smol {
+ () => {
+ 2u8.to_ne_bytes();
+ u8::from_ne_bytes(todo!());
+
+ 2u8.to_le_bytes();
+ u8::from_le_bytes(todo!());
+
+ 2u8.to_be_bytes();
+ u8::from_be_bytes(todo!());
+ };
+}
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+fn host() { fn_body!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::little_endian_bytes)]
+fn little() { fn_body!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::big_endian_bytes)]
+fn big() { fn_body!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+#[warn(clippy::big_endian_bytes)]
+fn host_encourage_little() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+#[warn(clippy::little_endian_bytes)]
+fn host_encourage_big() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+#[warn(clippy::little_endian_bytes)]
+#[warn(clippy::big_endian_bytes)]
+fn no_help() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::little_endian_bytes)]
+#[warn(clippy::big_endian_bytes)]
+fn little_encourage_host() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+#[warn(clippy::little_endian_bytes)]
+fn little_encourage_big() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::big_endian_bytes)]
+#[warn(clippy::little_endian_bytes)]
+fn big_encourage_host() { fn_body_smol!(); }
+
+#[rustfmt::skip]
+#[warn(clippy::host_endian_bytes)]
+#[warn(clippy::big_endian_bytes)]
+fn big_encourage_little() { fn_body_smol!(); }
diff --git a/src/tools/clippy/tests/ui/endian_bytes.stderr b/src/tools/clippy/tests/ui/endian_bytes.stderr
new file mode 100644
index 000000000..5e64ea5b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/endian_bytes.stderr
@@ -0,0 +1,1031 @@
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:7:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: `-D clippy::host-endian-bytes` implied by `-D warnings`
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:8:9
+ |
+LL | 2i8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u16::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:9:9
+ |
+LL | 2u16.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i16::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:10:9
+ |
+LL | 2i16.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u32::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:11:9
+ |
+LL | 2u32.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i32::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:12:9
+ |
+LL | 2i32.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u64::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:13:9
+ |
+LL | 2u64.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i64::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:14:9
+ |
+LL | 2i64.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u128::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:15:9
+ |
+LL | 2u128.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i128::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:16:9
+ |
+LL | 2i128.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `f32::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:17:9
+ |
+LL | 2.0f32.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `f64::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:18:9
+ |
+LL | 2.0f64.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `usize::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:19:9
+ |
+LL | 2usize.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `isize::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:20:9
+ |
+LL | 2isize.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:21:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:22:9
+ |
+LL | i8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u16::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:23:9
+ |
+LL | u16::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i16::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:24:9
+ |
+LL | i16::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u32::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:25:9
+ |
+LL | u32::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i32::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:26:9
+ |
+LL | i32::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u64::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:27:9
+ |
+LL | u64::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i64::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:28:9
+ |
+LL | i64::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u128::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:29:9
+ |
+LL | u128::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i128::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:30:9
+ |
+LL | i128::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `usize::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:31:9
+ |
+LL | usize::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `isize::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:32:9
+ |
+LL | isize::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `f32::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:33:9
+ |
+LL | f32::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `f64::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:34:9
+ |
+LL | f64::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: specify the desired endianness explicitly
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:36:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: `-D clippy::little-endian-bytes` implied by `-D warnings`
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:37:9
+ |
+LL | 2i8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u16::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:38:9
+ |
+LL | 2u16.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i16::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:39:9
+ |
+LL | 2i16.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u32::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:40:9
+ |
+LL | 2u32.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i32::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:41:9
+ |
+LL | 2i32.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u64::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:42:9
+ |
+LL | 2u64.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i64::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:43:9
+ |
+LL | 2i64.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u128::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:44:9
+ |
+LL | 2u128.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `i128::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:45:9
+ |
+LL | 2i128.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `f32::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:46:9
+ |
+LL | 2.0f32.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `f64::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:47:9
+ |
+LL | 2.0f64.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `usize::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:48:9
+ |
+LL | 2usize.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `isize::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:49:9
+ |
+LL | 2isize.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:50:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:51:9
+ |
+LL | i8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u16::from_le_bytes`
+ --> $DIR/endian_bytes.rs:52:9
+ |
+LL | u16::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i16::from_le_bytes`
+ --> $DIR/endian_bytes.rs:53:9
+ |
+LL | i16::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u32::from_le_bytes`
+ --> $DIR/endian_bytes.rs:54:9
+ |
+LL | u32::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i32::from_le_bytes`
+ --> $DIR/endian_bytes.rs:55:9
+ |
+LL | i32::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u64::from_le_bytes`
+ --> $DIR/endian_bytes.rs:56:9
+ |
+LL | u64::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i64::from_le_bytes`
+ --> $DIR/endian_bytes.rs:57:9
+ |
+LL | i64::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u128::from_le_bytes`
+ --> $DIR/endian_bytes.rs:58:9
+ |
+LL | u128::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `i128::from_le_bytes`
+ --> $DIR/endian_bytes.rs:59:9
+ |
+LL | i128::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `usize::from_le_bytes`
+ --> $DIR/endian_bytes.rs:60:9
+ |
+LL | usize::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `isize::from_le_bytes`
+ --> $DIR/endian_bytes.rs:61:9
+ |
+LL | isize::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `f32::from_le_bytes`
+ --> $DIR/endian_bytes.rs:62:9
+ |
+LL | f32::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `f64::from_le_bytes`
+ --> $DIR/endian_bytes.rs:63:9
+ |
+LL | f64::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little() { fn_body!(); }
+ | ---------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:70:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:71:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_be_bytes` method
+ --> $DIR/endian_bytes.rs:76:9
+ |
+LL | 2u8.to_be_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_le_bytes` instead
+ = note: `-D clippy::big-endian-bytes` implied by `-D warnings`
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_be_bytes`
+ --> $DIR/endian_bytes.rs:77:9
+ |
+LL | u8::from_be_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:70:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:71:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:73:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:74:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn host_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:70:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:71:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:73:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:74:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_be_bytes` method
+ --> $DIR/endian_bytes.rs:76:9
+ |
+LL | 2u8.to_be_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_be_bytes`
+ --> $DIR/endian_bytes.rs:77:9
+ |
+LL | u8::from_be_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn no_help() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:73:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:74:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_be_bytes` method
+ --> $DIR/endian_bytes.rs:76:9
+ |
+LL | 2u8.to_be_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_be_bytes`
+ --> $DIR/endian_bytes.rs:77:9
+ |
+LL | u8::from_be_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:70:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:71:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:73:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:74:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn little_encourage_big() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_be_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_le_bytes` method
+ --> $DIR/endian_bytes.rs:73:9
+ |
+LL | 2u8.to_le_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_le_bytes`
+ --> $DIR/endian_bytes.rs:74:9
+ |
+LL | u8::from_le_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_be_bytes` method
+ --> $DIR/endian_bytes.rs:76:9
+ |
+LL | 2u8.to_be_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_be_bytes`
+ --> $DIR/endian_bytes.rs:77:9
+ |
+LL | u8::from_be_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_host() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use the native endianness instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_ne_bytes` method
+ --> $DIR/endian_bytes.rs:70:9
+ |
+LL | 2u8.to_ne_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_ne_bytes`
+ --> $DIR/endian_bytes.rs:71:9
+ |
+LL | u8::from_ne_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the `u8::to_be_bytes` method
+ --> $DIR/endian_bytes.rs:76:9
+ |
+LL | 2u8.to_be_bytes();
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::to_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: usage of the function `u8::from_be_bytes`
+ --> $DIR/endian_bytes.rs:77:9
+ |
+LL | u8::from_be_bytes(todo!());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn big_encourage_little() { fn_body_smol!(); }
+ | --------------- in this macro invocation
+ |
+ = help: use `u8::from_le_bytes` instead
+ = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 86 previous errors
+
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
index f17556ea9..abe42a230 100644
--- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
@@ -1,4 +1,4 @@
-//@ignore-x86
+//@ignore-target-x86
#![warn(clippy::enum_clike_unportable_variant)]
#![allow(unused, non_upper_case_globals)]
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.rs b/src/tools/clippy/tests/ui/eprint_with_newline.rs
index de5e121be..8389806c8 100644
--- a/src/tools/clippy/tests/ui/eprint_with_newline.rs
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.rs
@@ -44,7 +44,7 @@ fn main() {
// Don't warn on CRLF (#4208)
eprint!("\r\n");
eprint!("foo\r\n");
- eprint!("\\r\n"); //~ ERROR
+ eprint!("\\r\n");
eprint!("foo\rbar\n");
// Ignore expanded format strings
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.stderr b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
index 0eefb9f0c..0a6bdf15d 100644
--- a/src/tools/clippy/tests/ui/eprint_with_newline.stderr
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
@@ -62,13 +62,13 @@ LL + eprintln!();
error: using `eprint!()` with a format string that ends in a single newline
--> $DIR/eprint_with_newline.rs:28:5
|
-LL | eprint!("//n"); // should fail
+LL | eprint!("///n"); // should fail
| ^^^^^^^^^^^^^^^
|
help: use `eprintln!` instead
|
-LL - eprint!("//n"); // should fail
-LL + eprintln!("/"); // should fail
+LL - eprint!("///n"); // should fail
+LL + eprintln!("//"); // should fail
|
error: using `eprint!()` with a format string that ends in a single newline
@@ -104,13 +104,13 @@ LL ~
error: using `eprint!()` with a format string that ends in a single newline
--> $DIR/eprint_with_newline.rs:47:5
|
-LL | eprint!("/r/n"); //~ ERROR
+LL | eprint!("//r/n");
| ^^^^^^^^^^^^^^^^
|
help: use `eprintln!` instead
|
-LL - eprint!("/r/n"); //~ ERROR
-LL + eprintln!("/r"); //~ ERROR
+LL - eprint!("//r/n");
+LL + eprintln!("//r");
|
error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/eq_op.rs b/src/tools/clippy/tests/ui/eq_op.rs
index cdd33ebe5..e973e5ba2 100644
--- a/src/tools/clippy/tests/ui/eq_op.rs
+++ b/src/tools/clippy/tests/ui/eq_op.rs
@@ -1,5 +1,3 @@
-//@compile-flags: --test
-
#![warn(clippy::eq_op)]
#![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)]
#![allow(clippy::suspicious_xor_used_as_pow)]
@@ -12,6 +10,8 @@ fn main() {
let _ = false != false;
let _ = 1.5 < 1.5;
let _ = 1u64 >= 1u64;
+ let x = f32::NAN;
+ let _ = x != x;
// casts, methods, parentheses
let _ = (1u32 as u64) & (1u32 as u64);
diff --git a/src/tools/clippy/tests/ui/eq_op.stderr b/src/tools/clippy/tests/ui/eq_op.stderr
index d365ab27e..c7fa253bd 100644
--- a/src/tools/clippy/tests/ui/eq_op.stderr
+++ b/src/tools/clippy/tests/ui/eq_op.stderr
@@ -1,5 +1,5 @@
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:9:13
+ --> $DIR/eq_op.rs:7:13
|
LL | let _ = 1 == 1;
| ^^^^^^
@@ -7,29 +7,37 @@ LL | let _ = 1 == 1;
= note: `-D clippy::eq-op` implied by `-D warnings`
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:10:13
+ --> $DIR/eq_op.rs:8:13
|
LL | let _ = "no" == "no";
| ^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:12:13
+ --> $DIR/eq_op.rs:10:13
|
LL | let _ = false != false;
| ^^^^^^^^^^^^^^
error: equal expressions as operands to `<`
- --> $DIR/eq_op.rs:13:13
+ --> $DIR/eq_op.rs:11:13
|
LL | let _ = 1.5 < 1.5;
| ^^^^^^^^^
error: equal expressions as operands to `>=`
- --> $DIR/eq_op.rs:14:13
+ --> $DIR/eq_op.rs:12:13
|
LL | let _ = 1u64 >= 1u64;
| ^^^^^^^^^^^^
+error: equal expressions as operands to `!=`
+ --> $DIR/eq_op.rs:14:13
+ |
+LL | let _ = x != x;
+ | ^^^^^^
+ |
+ = note: if you intended to check if the operand is NaN, use `.is_nan()` instead
+
error: equal expressions as operands to `&`
--> $DIR/eq_op.rs:17:13
|
@@ -168,5 +176,5 @@ error: equal expressions as operands to `==`
LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 28 previous errors
+error: aborting due to 29 previous errors
diff --git a/src/tools/clippy/tests/ui/eq_op_macros.rs b/src/tools/clippy/tests/ui/eq_op_macros.rs
index 6b5b31a1a..482406772 100644
--- a/src/tools/clippy/tests/ui/eq_op_macros.rs
+++ b/src/tools/clippy/tests/ui/eq_op_macros.rs
@@ -1,4 +1,5 @@
#![warn(clippy::eq_op)]
+#![allow(clippy::useless_vec)]
// lint also in macro definition
macro_rules! assert_in_macro_def {
diff --git a/src/tools/clippy/tests/ui/eq_op_macros.stderr b/src/tools/clippy/tests/ui/eq_op_macros.stderr
index cd9f1826e..cb9b0c018 100644
--- a/src/tools/clippy/tests/ui/eq_op_macros.stderr
+++ b/src/tools/clippy/tests/ui/eq_op_macros.stderr
@@ -1,5 +1,5 @@
error: identical args used in this `assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:7:20
+ --> $DIR/eq_op_macros.rs:8:20
|
LL | assert_eq!(a, a);
| ^^^^
@@ -11,7 +11,7 @@ LL | assert_in_macro_def!();
= note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
error: identical args used in this `assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:8:20
+ --> $DIR/eq_op_macros.rs:9:20
|
LL | assert_ne!(a, a);
| ^^^^
@@ -22,7 +22,7 @@ LL | assert_in_macro_def!();
= note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
error: identical args used in this `debug_assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:9:26
+ --> $DIR/eq_op_macros.rs:10:26
|
LL | debug_assert_eq!(a, a);
| ^^^^
@@ -33,7 +33,7 @@ LL | assert_in_macro_def!();
= note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
error: identical args used in this `debug_assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:10:26
+ --> $DIR/eq_op_macros.rs:11:26
|
LL | debug_assert_ne!(a, a);
| ^^^^
@@ -44,49 +44,49 @@ LL | assert_in_macro_def!();
= note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
error: identical args used in this `assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:22:16
+ --> $DIR/eq_op_macros.rs:23:16
|
LL | assert_eq!(a, a);
| ^^^^
error: identical args used in this `assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:23:16
+ --> $DIR/eq_op_macros.rs:24:16
|
LL | assert_eq!(a + 1, a + 1);
| ^^^^^^^^^^^^
error: identical args used in this `assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:30:16
+ --> $DIR/eq_op_macros.rs:31:16
|
LL | assert_ne!(a, a);
| ^^^^
error: identical args used in this `assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:31:16
+ --> $DIR/eq_op_macros.rs:32:16
|
LL | assert_ne!(a + 1, a + 1);
| ^^^^^^^^^^^^
error: identical args used in this `debug_assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:38:22
+ --> $DIR/eq_op_macros.rs:39:22
|
LL | debug_assert_eq!(a, a);
| ^^^^
error: identical args used in this `debug_assert_eq!` macro call
- --> $DIR/eq_op_macros.rs:39:22
+ --> $DIR/eq_op_macros.rs:40:22
|
LL | debug_assert_eq!(a + 1, a + 1);
| ^^^^^^^^^^^^
error: identical args used in this `debug_assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:46:22
+ --> $DIR/eq_op_macros.rs:47:22
|
LL | debug_assert_ne!(a, a);
| ^^^^
error: identical args used in this `debug_assert_ne!` macro call
- --> $DIR/eq_op_macros.rs:47:22
+ --> $DIR/eq_op_macros.rs:48:22
|
LL | debug_assert_ne!(a + 1, a + 1);
| ^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed
index 53e62760b..6cc070fb5 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.fixed
+++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed
@@ -1,7 +1,12 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
-
-#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
+//@aux-build:proc_macros.rs:proc-macro
+
+#![allow(
+ unused_variables,
+ dead_code,
+ clippy::derive_partial_eq_without_eq,
+ clippy::needless_if
+)]
#![warn(clippy::equatable_if_let)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs
index 55918a5bb..f00a129be 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.rs
+++ b/src/tools/clippy/tests/ui/equatable_if_let.rs
@@ -1,7 +1,12 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
-
-#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
+//@aux-build:proc_macros.rs:proc-macro
+
+#![allow(
+ unused_variables,
+ dead_code,
+ clippy::derive_partial_eq_without_eq,
+ clippy::needless_if
+)]
#![warn(clippy::equatable_if_let)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr
index a72d87bb7..649495dde 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.stderr
+++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr
@@ -1,5 +1,5 @@
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:60:8
+ --> $DIR/equatable_if_let.rs:65:8
|
LL | if let 2 = a {}
| ^^^^^^^^^ help: try: `a == 2`
@@ -7,79 +7,79 @@ LL | if let 2 = a {}
= note: `-D clippy::equatable-if-let` implied by `-D warnings`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:61:8
+ --> $DIR/equatable_if_let.rs:66:8
|
LL | if let Ordering::Greater = a.cmp(&b) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:62:8
+ --> $DIR/equatable_if_let.rs:67:8
|
LL | if let Some(2) = c {}
| ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:63:8
+ --> $DIR/equatable_if_let.rs:68:8
|
LL | if let Struct { a: 2, b: false } = d {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:64:8
+ --> $DIR/equatable_if_let.rs:69:8
|
LL | if let Enum::TupleVariant(32, 64) = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:65:8
+ --> $DIR/equatable_if_let.rs:70:8
|
LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:66:8
+ --> $DIR/equatable_if_let.rs:71:8
|
LL | if let Enum::UnitVariant = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:67:8
+ --> $DIR/equatable_if_let.rs:72:8
|
LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:76:8
+ --> $DIR/equatable_if_let.rs:81:8
|
LL | if let NotPartialEq::A = f {}
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:77:8
+ --> $DIR/equatable_if_let.rs:82:8
|
LL | if let NotStructuralEq::A = g {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:78:8
+ --> $DIR/equatable_if_let.rs:83:8
|
LL | if let Some(NotPartialEq::A) = Some(f) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:79:8
+ --> $DIR/equatable_if_let.rs:84:8
|
LL | if let Some(NotStructuralEq::A) = Some(g) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:80:8
+ --> $DIR/equatable_if_let.rs:85:8
|
LL | if let NoPartialEqStruct { a: 2, b: false } = h {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:82:8
+ --> $DIR/equatable_if_let.rs:87:8
|
LL | if let inline!("abc") = "abc" {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")`
diff --git a/src/tools/clippy/tests/ui/err_expect.fixed b/src/tools/clippy/tests/ui/err_expect.fixed
index 6ade6f546..46e2816da 100644
--- a/src/tools/clippy/tests/ui/err_expect.fixed
+++ b/src/tools/clippy/tests/ui/err_expect.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused)]
+#![allow(unused, clippy::unnecessary_literal_unwrap)]
struct MyTypeNonDebug;
diff --git a/src/tools/clippy/tests/ui/err_expect.rs b/src/tools/clippy/tests/ui/err_expect.rs
index a93fb5949..b9446034d 100644
--- a/src/tools/clippy/tests/ui/err_expect.rs
+++ b/src/tools/clippy/tests/ui/err_expect.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused)]
+#![allow(unused, clippy::unnecessary_literal_unwrap)]
struct MyTypeNonDebug;
diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed
index b1baf462c..bf44bcb56 100644
--- a/src/tools/clippy/tests/ui/eta.fixed
+++ b/src/tools/clippy/tests/ui/eta.fixed
@@ -7,7 +7,8 @@
clippy::no_effect,
clippy::option_map_unit_fn,
clippy::redundant_closure_call,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
use std::path::{Path, PathBuf};
@@ -46,6 +47,12 @@ fn main() {
// issue #7224
let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+
+ // issue #10684
+ fn test<T>(x: impl Fn(usize, usize) -> T) -> T {
+ x(1, 2)
+ }
+ test(|start, end| start..=end);
}
trait TestTrait {
diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs
index e113c3d6c..b2af4bf09 100644
--- a/src/tools/clippy/tests/ui/eta.rs
+++ b/src/tools/clippy/tests/ui/eta.rs
@@ -7,7 +7,8 @@
clippy::no_effect,
clippy::option_map_unit_fn,
clippy::redundant_closure_call,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
use std::path::{Path, PathBuf};
@@ -46,6 +47,12 @@ fn main() {
// issue #7224
let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+
+ // issue #10684
+ fn test<T>(x: impl Fn(usize, usize) -> T) -> T {
+ x(1, 2)
+ }
+ test(|start, end| start..=end);
}
trait TestTrait {
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
index a521fb868..0ac0b901d 100644
--- a/src/tools/clippy/tests/ui/eta.stderr
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -1,5 +1,5 @@
error: redundant closure
- --> $DIR/eta.rs:28:27
+ --> $DIR/eta.rs:29:27
|
LL | let a = Some(1u8).map(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@@ -7,31 +7,31 @@ LL | let a = Some(1u8).map(|a| foo(a));
= note: `-D clippy::redundant-closure` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:32:40
+ --> $DIR/eta.rs:33:40
|
LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
error: redundant closure
- --> $DIR/eta.rs:33:35
+ --> $DIR/eta.rs:34:35
|
LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
| ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
error: redundant closure
- --> $DIR/eta.rs:34:26
+ --> $DIR/eta.rs:35:26
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
error: redundant closure
- --> $DIR/eta.rs:41:27
+ --> $DIR/eta.rs:42:27
|
LL | let e = Some(1u8).map(|a| generic(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
error: redundant closure
- --> $DIR/eta.rs:87:51
+ --> $DIR/eta.rs:94:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@@ -39,121 +39,121 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:88:51
+ --> $DIR/eta.rs:95:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
error: redundant closure
- --> $DIR/eta.rs:90:42
+ --> $DIR/eta.rs:97:42
|
LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
error: redundant closure
- --> $DIR/eta.rs:94:29
+ --> $DIR/eta.rs:101:29
|
LL | let e = Some("str").map(|s| s.to_string());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
error: redundant closure
- --> $DIR/eta.rs:95:27
+ --> $DIR/eta.rs:102:27
|
LL | let e = Some('a').map(|s| s.to_uppercase());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
error: redundant closure
- --> $DIR/eta.rs:97:65
+ --> $DIR/eta.rs:104:65
|
LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure
- --> $DIR/eta.rs:160:22
+ --> $DIR/eta.rs:167:22
|
LL | requires_fn_once(|| x());
| ^^^^^^ help: replace the closure with the function itself: `x`
error: redundant closure
- --> $DIR/eta.rs:167:27
+ --> $DIR/eta.rs:174:27
|
LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure
- --> $DIR/eta.rs:172:27
+ --> $DIR/eta.rs:179:27
|
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
error: redundant closure
- --> $DIR/eta.rs:204:28
+ --> $DIR/eta.rs:211:28
|
LL | x.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:205:28
+ --> $DIR/eta.rs:212:28
|
LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:206:28
+ --> $DIR/eta.rs:213:28
|
LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure
- --> $DIR/eta.rs:213:21
+ --> $DIR/eta.rs:220:21
|
LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure
- --> $DIR/eta.rs:217:21
+ --> $DIR/eta.rs:224:21
|
LL | Some(1).map(|n| in_loop(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
error: redundant closure
- --> $DIR/eta.rs:310:18
+ --> $DIR/eta.rs:317:18
|
LL | takes_fn_mut(|| f());
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
error: redundant closure
- --> $DIR/eta.rs:313:19
+ --> $DIR/eta.rs:320:19
|
LL | takes_fn_once(|| f());
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
error: redundant closure
- --> $DIR/eta.rs:317:26
+ --> $DIR/eta.rs:324:26
|
LL | move || takes_fn_mut(|| f_used_once())
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
error: redundant closure
- --> $DIR/eta.rs:329:19
+ --> $DIR/eta.rs:336:19
|
LL | array_opt.map(|a| a.as_slice());
| ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
error: redundant closure
- --> $DIR/eta.rs:332:19
+ --> $DIR/eta.rs:339:19
|
LL | slice_opt.map(|s| s.len());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
error: redundant closure
- --> $DIR/eta.rs:335:17
+ --> $DIR/eta.rs:342:17
|
LL | ptr_opt.map(|p| p.is_null());
| ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
error: redundant closure
- --> $DIR/eta.rs:339:17
+ --> $DIR/eta.rs:346:17
|
LL | dyn_opt.map(|d| d.method_on_dyn());
| ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed
index 0a0795738..7bb4da453 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.fixed
+++ b/src/tools/clippy/tests/ui/excessive_precision.fixed
@@ -1,6 +1,12 @@
//@run-rustfix
#![warn(clippy::excessive_precision)]
-#![allow(dead_code, unused_variables, clippy::print_literal)]
+#![allow(
+ dead_code,
+ overflowing_literals,
+ unused_variables,
+ clippy::print_literal,
+ clippy::useless_vec
+)]
fn main() {
// Consts
@@ -66,4 +72,11 @@ fn main() {
// issue #7745
let _ = 0_f64;
+
+ // issue #9910
+ const INF1: f32 = 1.0e+33f32;
+ const INF2: f64 = 1.0e+3300f64;
+ const NEG_INF1: f32 = -1.0e+33f32;
+ const NEG_INF2: f64 = -1.0e+3300f64;
+ const NEG_INF3: f32 = -3.40282357e+38_f32;
}
diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs
index 62a832caa..e8d6ab687 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.rs
+++ b/src/tools/clippy/tests/ui/excessive_precision.rs
@@ -1,6 +1,12 @@
//@run-rustfix
#![warn(clippy::excessive_precision)]
-#![allow(dead_code, unused_variables, clippy::print_literal)]
+#![allow(
+ dead_code,
+ overflowing_literals,
+ unused_variables,
+ clippy::print_literal,
+ clippy::useless_vec
+)]
fn main() {
// Consts
@@ -66,4 +72,11 @@ fn main() {
// issue #7745
let _ = 1.000_000_000_000_001e-324_f64;
+
+ // issue #9910
+ const INF1: f32 = 1.0e+33f32;
+ const INF2: f64 = 1.0e+3300f64;
+ const NEG_INF1: f32 = -1.0e+33f32;
+ const NEG_INF2: f64 = -1.0e+3300f64;
+ const NEG_INF3: f32 = -3.40282357e+38_f32;
}
diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr
index 42d9d4de1..348ad183d 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.stderr
+++ b/src/tools/clippy/tests/ui/excessive_precision.stderr
@@ -1,5 +1,5 @@
error: float has excessive precision
- --> $DIR/excessive_precision.rs:15:26
+ --> $DIR/excessive_precision.rs:21:26
|
LL | const BAD32_1: f32 = 0.123_456_789_f32;
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
@@ -7,85 +7,85 @@ LL | const BAD32_1: f32 = 0.123_456_789_f32;
= note: `-D clippy::excessive-precision` implied by `-D warnings`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:16:26
+ --> $DIR/excessive_precision.rs:22:26
|
LL | const BAD32_2: f32 = 0.123_456_789;
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:17:26
+ --> $DIR/excessive_precision.rs:23:26
|
LL | const BAD32_3: f32 = 0.100_000_000_000_1;
| ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:18:29
+ --> $DIR/excessive_precision.rs:24:29
|
LL | const BAD32_EDGE: f32 = 1.000_000_9;
| ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:22:26
+ --> $DIR/excessive_precision.rs:28:26
|
LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:25:22
+ --> $DIR/excessive_precision.rs:31:22
|
LL | println!("{:?}", 8.888_888_888_888_888_888_888);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:36:22
+ --> $DIR/excessive_precision.rs:42:22
|
LL | let bad32: f32 = 1.123_456_789;
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:37:26
+ --> $DIR/excessive_precision.rs:43:26
|
LL | let bad32_suf: f32 = 1.123_456_789_f32;
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:38:21
+ --> $DIR/excessive_precision.rs:44:21
|
LL | let bad32_inf = 1.123_456_789_f32;
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:48:36
+ --> $DIR/excessive_precision.rs:54:36
|
LL | let bad_vec32: Vec<f32> = vec![0.123_456_789];
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:49:36
+ --> $DIR/excessive_precision.rs:55:36
|
LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:53:24
+ --> $DIR/excessive_precision.rs:59:24
|
LL | let bad_e32: f32 = 1.123_456_788_888e-10;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:56:27
+ --> $DIR/excessive_precision.rs:62:27
|
LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:65:13
+ --> $DIR/excessive_precision.rs:71:13
|
LL | let _ = 2.225_073_858_507_201_1e-308_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64`
error: float has excessive precision
- --> $DIR/excessive_precision.rs:68:13
+ --> $DIR/excessive_precision.rs:74:13
|
LL | let _ = 1.000_000_000_000_001e-324_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64`
diff --git a/src/tools/clippy/tests/ui/expect.rs b/src/tools/clippy/tests/ui/expect.rs
index d742595e1..1588579bb 100644
--- a/src/tools/clippy/tests/ui/expect.rs
+++ b/src/tools/clippy/tests/ui/expect.rs
@@ -1,4 +1,5 @@
#![warn(clippy::expect_used)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn expect_option() {
let opt = Some(0);
diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr
index c08e0dbbf..be340340d 100644
--- a/src/tools/clippy/tests/ui/expect.stderr
+++ b/src/tools/clippy/tests/ui/expect.stderr
@@ -1,5 +1,5 @@
error: used `expect()` on an `Option` value
- --> $DIR/expect.rs:5:13
+ --> $DIR/expect.rs:6:13
|
LL | let _ = opt.expect("");
| ^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let _ = opt.expect("");
= note: `-D clippy::expect-used` implied by `-D warnings`
error: used `expect()` on a `Result` value
- --> $DIR/expect.rs:10:13
+ --> $DIR/expect.rs:11:13
|
LL | let _ = res.expect("");
| ^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | let _ = res.expect("");
= help: if this value is an `Err`, it will panic
error: used `expect_err()` on a `Result` value
- --> $DIR/expect.rs:11:13
+ --> $DIR/expect.rs:12:13
|
LL | let _ = res.expect_err("");
| ^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed
index 8e97054fb..73c6c97de 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed
@@ -1,6 +1,10 @@
//@run-rustfix
#![warn(clippy::expect_fun_call)]
-#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)]
+#![allow(
+ clippy::to_string_in_format_args,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
+)]
/// Checks implementation of the `EXPECT_FUN_CALL` lint
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs
index 31e6bcc7f..a78613863 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.rs
+++ b/src/tools/clippy/tests/ui/expect_fun_call.rs
@@ -1,6 +1,10 @@
//@run-rustfix
#![warn(clippy::expect_fun_call)]
-#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)]
+#![allow(
+ clippy::to_string_in_format_args,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
+)]
/// Checks implementation of the `EXPECT_FUN_CALL` lint
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr
index cb55e32ae..36fb0e5de 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr
@@ -1,5 +1,5 @@
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:34:26
+ --> $DIR/expect_fun_call.rs:38:26
|
LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
@@ -7,85 +7,85 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code
= note: `-D clippy::expect-fun-call` implied by `-D warnings`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:37:26
+ --> $DIR/expect_fun_call.rs:41:26
|
LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:40:37
+ --> $DIR/expect_fun_call.rs:44:37
|
LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:50:25
+ --> $DIR/expect_fun_call.rs:54:25
|
LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:53:25
+ --> $DIR/expect_fun_call.rs:57:25
|
LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:65:17
+ --> $DIR/expect_fun_call.rs:69:17
|
LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:86:21
+ --> $DIR/expect_fun_call.rs:90:21
|
LL | Some("foo").expect(&get_string());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:87:21
+ --> $DIR/expect_fun_call.rs:91:21
|
LL | Some("foo").expect(get_string().as_ref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:88:21
+ --> $DIR/expect_fun_call.rs:92:21
|
LL | Some("foo").expect(get_string().as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:90:21
+ --> $DIR/expect_fun_call.rs:94:21
|
LL | Some("foo").expect(get_static_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:91:21
+ --> $DIR/expect_fun_call.rs:95:21
|
LL | Some("foo").expect(get_non_static_str(&0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:95:16
+ --> $DIR/expect_fun_call.rs:99:16
|
LL | Some(true).expect(&format!("key {}, {}", 1, 2));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:101:17
+ --> $DIR/expect_fun_call.rs:105:17
|
LL | opt_ref.expect(&format!("{:?}", opt_ref));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:105:20
+ --> $DIR/expect_fun_call.rs:109:20
|
LL | format_capture.expect(&format!("{error_code}"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}"))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:108:30
+ --> $DIR/expect_fun_call.rs:112:30
|
LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}, {}", 1))`
diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs
index 0415e33b3..2460f3343 100644
--- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs
+++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs
@@ -1,4 +1,3 @@
-// check-pass
#![feature(lint_reasons)]
//! This file tests the `#[expect]` attribute implementation for tool lints. The same
//! file is used to test clippy and rustdoc. Any changes to this file should be synced
@@ -12,6 +11,7 @@
//! This test can't cover every lint from Clippy, rustdoc and potentially other
//! tools that will be developed. This therefore only tests a small subset of lints
#![expect(rustdoc::missing_crate_level_docs)]
+#![allow(clippy::needless_if)]
mod rustc_ok {
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
index 46565a97f..e02b8f62b 100644
--- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
@@ -1,5 +1,5 @@
#![warn(clippy::explicit_counter_loop)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
@@ -23,6 +23,54 @@ fn main() {
for _v in vec {
_index += 1;
}
+
+ let vec = [1, 2, 3, 4];
+ // Potential false positives
+ let mut _index = 0;
+ _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ _index += 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index = 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ let mut _index = 0;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index = 0;
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ if false {
+ _index = 0
+ };
+ for _v in &vec {
+ _index += 1
+ }
}
mod issue_1219 {
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
index d3f3c626b..0677e4d78 100644
--- a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
@@ -25,31 +25,31 @@ LL | for _v in vec {
| ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()`
error: the variable `count` is used as a loop counter
- --> $DIR/explicit_counter_loop.rs:62:9
+ --> $DIR/explicit_counter_loop.rs:110:9
|
LL | for ch in text.chars() {
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
error: the variable `count` is used as a loop counter
- --> $DIR/explicit_counter_loop.rs:73:9
+ --> $DIR/explicit_counter_loop.rs:121:9
|
LL | for ch in text.chars() {
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
error: the variable `count` is used as a loop counter
- --> $DIR/explicit_counter_loop.rs:131:9
+ --> $DIR/explicit_counter_loop.rs:179:9
|
LL | for _i in 3..10 {
| ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
error: the variable `idx_usize` is used as a loop counter
- --> $DIR/explicit_counter_loop.rs:171:9
+ --> $DIR/explicit_counter_loop.rs:219:9
|
LL | for _item in slice {
| ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()`
error: the variable `idx_u32` is used as a loop counter
- --> $DIR/explicit_counter_loop.rs:183:9
+ --> $DIR/explicit_counter_loop.rs:231:9
|
LL | for _item in slice {
| ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())`
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
index 60482c66d..4d72b58cd 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
@@ -1,12 +1,14 @@
//@run-rustfix
#![warn(clippy::explicit_deref_methods)]
-#![allow(unused_variables)]
+#![allow(unused_variables, unused_must_use)]
#![allow(
clippy::borrow_deref_ref,
suspicious_double_ref_op,
clippy::explicit_auto_deref,
clippy::needless_borrow,
- clippy::uninlined_format_args
+ clippy::no_effect,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
)]
use std::ops::{Deref, DerefMut};
@@ -28,6 +30,22 @@ impl Deref for CustomVec {
}
}
+struct Aaa;
+
+impl Deref for Aaa {
+ type Target = ();
+
+ fn deref(&self) -> &Self::Target {
+ todo!();
+ }
+}
+
+impl DerefMut for Aaa {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ todo!();
+ }
+}
+
fn main() {
let a: &mut String = &mut String::from("foo");
@@ -58,6 +76,17 @@ fn main() {
let opt_a = Some(a.clone());
let b = &*opt_a.unwrap();
+ // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified
+ // syntax
+
+ Aaa::deref(&Aaa);
+ Aaa::deref_mut(&mut Aaa);
+ <Aaa as Deref>::deref(&Aaa);
+ <Aaa as DerefMut>::deref_mut(&mut Aaa);
+ let mut aaa = Aaa;
+ Aaa::deref(&aaa);
+ Aaa::deref_mut(&mut aaa);
+
// following should not require linting
let cv = CustomVec(vec![0, 42]);
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
index e3613e216..fcd945de3 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
@@ -1,12 +1,14 @@
//@run-rustfix
#![warn(clippy::explicit_deref_methods)]
-#![allow(unused_variables)]
+#![allow(unused_variables, unused_must_use)]
#![allow(
clippy::borrow_deref_ref,
suspicious_double_ref_op,
clippy::explicit_auto_deref,
clippy::needless_borrow,
- clippy::uninlined_format_args
+ clippy::no_effect,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
)]
use std::ops::{Deref, DerefMut};
@@ -28,6 +30,22 @@ impl Deref for CustomVec {
}
}
+struct Aaa;
+
+impl Deref for Aaa {
+ type Target = ();
+
+ fn deref(&self) -> &Self::Target {
+ todo!();
+ }
+}
+
+impl DerefMut for Aaa {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ todo!();
+ }
+}
+
fn main() {
let a: &mut String = &mut String::from("foo");
@@ -58,6 +76,17 @@ fn main() {
let opt_a = Some(a.clone());
let b = opt_a.unwrap().deref();
+ // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified
+ // syntax
+
+ Aaa::deref(&Aaa);
+ Aaa::deref_mut(&mut Aaa);
+ <Aaa as Deref>::deref(&Aaa);
+ <Aaa as DerefMut>::deref_mut(&mut Aaa);
+ let mut aaa = Aaa;
+ Aaa::deref(&aaa);
+ Aaa::deref_mut(&mut aaa);
+
// following should not require linting
let cv = CustomVec(vec![0, 42]);
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
index 4b10ed137..d025035b7 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
@@ -1,5 +1,5 @@
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:36:19
+ --> $DIR/explicit_deref_methods.rs:54:19
|
LL | let b: &str = a.deref();
| ^^^^^^^^^ help: try this: `&*a`
@@ -7,67 +7,67 @@ LL | let b: &str = a.deref();
= note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
error: explicit `deref_mut` method call
- --> $DIR/explicit_deref_methods.rs:38:23
+ --> $DIR/explicit_deref_methods.rs:56:23
|
LL | let b: &mut str = a.deref_mut();
| ^^^^^^^^^^^^^ help: try this: `&mut **a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:41:39
+ --> $DIR/explicit_deref_methods.rs:59:39
|
LL | let b: String = format!("{}, {}", a.deref(), a.deref());
| ^^^^^^^^^ help: try this: `&*a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:41:50
+ --> $DIR/explicit_deref_methods.rs:59:50
|
LL | let b: String = format!("{}, {}", a.deref(), a.deref());
| ^^^^^^^^^ help: try this: `&*a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:43:20
+ --> $DIR/explicit_deref_methods.rs:61:20
|
LL | println!("{}", a.deref());
| ^^^^^^^^^ help: try this: `&*a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:46:11
+ --> $DIR/explicit_deref_methods.rs:64:11
|
LL | match a.deref() {
| ^^^^^^^^^ help: try this: `&*a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:50:28
+ --> $DIR/explicit_deref_methods.rs:68:28
|
LL | let b: String = concat(a.deref());
| ^^^^^^^^^ help: try this: `&*a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:52:13
+ --> $DIR/explicit_deref_methods.rs:70:13
|
LL | let b = just_return(a).deref();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:54:28
+ --> $DIR/explicit_deref_methods.rs:72:28
|
LL | let b: String = concat(just_return(a).deref());
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:56:19
+ --> $DIR/explicit_deref_methods.rs:74:19
|
LL | let b: &str = a.deref().deref();
| ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:59:13
+ --> $DIR/explicit_deref_methods.rs:77:13
|
LL | let b = opt_a.unwrap().deref();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
error: explicit `deref` method call
- --> $DIR/explicit_deref_methods.rs:85:31
+ --> $DIR/explicit_deref_methods.rs:114:31
|
LL | let b: &str = expr_deref!(a.deref());
| ^^^^^^^^^ help: try this: `&*a`
diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed
new file mode 100644
index 000000000..dcef63403
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed
@@ -0,0 +1,69 @@
+//@run-rustfix
+#![warn(clippy::explicit_into_iter_loop)]
+
+fn main() {
+ // Issue #4958
+ fn _takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for _ in iterator {}
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+
+ let mut t = T;
+ for _ in &t {}
+
+ let r = &t;
+ for _ in r {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ let rr = &&t;
+ for _ in rr.into_iter() {}
+
+ let mr = &mut t;
+ for _ in &*mr {}
+
+ struct U;
+ impl IntoIterator for &mut U {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+
+ let mut u = U;
+ for _ in &mut u {}
+
+ let mr = &mut u;
+ for _ in &mut *mr {}
+
+ // Issue #6900
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ for _ in S.into_iter::<u32>() {}
+}
diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs
new file mode 100644
index 000000000..bc048ed30
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs
@@ -0,0 +1,69 @@
+//@run-rustfix
+#![warn(clippy::explicit_into_iter_loop)]
+
+fn main() {
+ // Issue #4958
+ fn _takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for _ in iterator.into_iter() {}
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+
+ let mut t = T;
+ for _ in t.into_iter() {}
+
+ let r = &t;
+ for _ in r.into_iter() {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ let rr = &&t;
+ for _ in rr.into_iter() {}
+
+ let mr = &mut t;
+ for _ in mr.into_iter() {}
+
+ struct U;
+ impl IntoIterator for &mut U {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+
+ let mut u = U;
+ for _ in u.into_iter() {}
+
+ let mr = &mut u;
+ for _ in mr.into_iter() {}
+
+ // Issue #6900
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ for _ in S.into_iter::<u32>() {}
+}
diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr
new file mode 100644
index 000000000..fa89b884f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr
@@ -0,0 +1,40 @@
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:10:18
+ |
+LL | for _ in iterator.into_iter() {}
+ | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
+ |
+ = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:23:14
+ |
+LL | for _ in t.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:26:14
+ |
+LL | for _ in r.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:34:14
+ |
+LL | for _ in mr.into_iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*mr`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:46:14
+ |
+LL | for _ in u.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut u`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/explicit_into_iter_loop.rs:49:14
+ |
+LL | for _ in mr.into_iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *mr`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
new file mode 100644
index 000000000..746ef813c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
@@ -0,0 +1,154 @@
+//@run-rustfix
+#![deny(clippy::explicit_iter_loop)]
+#![allow(
+ clippy::linkedlist,
+ clippy::similar_names,
+ clippy::needless_borrow,
+ clippy::deref_addrof,
+ dead_code
+)]
+
+use core::slice;
+use std::collections::*;
+
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ for _ in &vec {}
+ for _ in &mut vec {}
+
+ let rvec = &vec;
+ for _ in rvec {}
+
+ let rmvec = &mut vec;
+ for _ in &*rmvec {}
+ for _ in &mut *rmvec {}
+
+ for _ in &vec {} // these are fine
+ for _ in &mut vec {} // these are fine
+
+ for _ in &[1, 2, 3] {}
+
+ for _ in &*(&mut [1, 2, 3]) {}
+
+ for _ in &[0; 32] {}
+ for _ in &[0; 33] {}
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _ in &ll {}
+ let rll = &ll;
+ for _ in rll {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _ in &vd {}
+ let rvd = &vd;
+ for _ in rvd {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _ in &bh {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _ in &hm {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _ in &bt {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _ in &hs {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _ in &bs {}
+
+ struct NoIntoIter();
+ impl NoIntoIter {
+ fn iter(&self) -> slice::Iter<u8> {
+ unimplemented!()
+ }
+
+ fn iter_mut(&mut self) -> slice::IterMut<u8> {
+ unimplemented!()
+ }
+ }
+ let mut x = NoIntoIter();
+ for _ in x.iter() {} // no error
+ for _ in x.iter_mut() {} // no error
+
+ struct IntoIterDiffTy;
+ impl IntoIterator for &'_ IntoIterDiffTy {
+ type Item = &'static ();
+ type IntoIter = core::slice::Iter<'static, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl IntoIterDiffTy {
+ fn iter(&self) -> core::slice::Iter<'static, i32> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffTy;
+ for _ in x.iter() {}
+
+ struct IntoIterDiffSig;
+ impl IntoIterator for &'_ IntoIterDiffSig {
+ type Item = &'static ();
+ type IntoIter = core::slice::Iter<'static, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl IntoIterDiffSig {
+ fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffSig;
+ for _ in x.iter(0) {}
+
+ struct IntoIterDiffLt<'a>(&'a ());
+ impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
+ type Item = &'a ();
+ type IntoIter = core::slice::Iter<'a, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl<'a> IntoIterDiffLt<'a> {
+ fn iter(&self) -> core::slice::Iter<'a, ()> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffLt(&());
+ for _ in x.iter() {}
+
+ struct CustomType;
+ impl<'a> IntoIterator for &'a CustomType {
+ type Item = &'a u32;
+ type IntoIter = core::slice::Iter<'a, u32>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl<'a> IntoIterator for &'a mut CustomType {
+ type Item = &'a mut u32;
+ type IntoIter = core::slice::IterMut<'a, u32>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl CustomType {
+ fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
+ panic!()
+ }
+
+ fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
+ panic!()
+ }
+ }
+ let mut x = CustomType;
+ for _ in &x {}
+ for _ in &mut x {}
+
+ let r = &x;
+ for _ in r {}
+}
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
new file mode 100644
index 000000000..fba230ee0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
@@ -0,0 +1,154 @@
+//@run-rustfix
+#![deny(clippy::explicit_iter_loop)]
+#![allow(
+ clippy::linkedlist,
+ clippy::similar_names,
+ clippy::needless_borrow,
+ clippy::deref_addrof,
+ dead_code
+)]
+
+use core::slice;
+use std::collections::*;
+
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ for _ in vec.iter() {}
+ for _ in vec.iter_mut() {}
+
+ let rvec = &vec;
+ for _ in rvec.iter() {}
+
+ let rmvec = &mut vec;
+ for _ in rmvec.iter() {}
+ for _ in rmvec.iter_mut() {}
+
+ for _ in &vec {} // these are fine
+ for _ in &mut vec {} // these are fine
+
+ for _ in [1, 2, 3].iter() {}
+
+ for _ in (&mut [1, 2, 3]).iter() {}
+
+ for _ in [0; 32].iter() {}
+ for _ in [0; 33].iter() {}
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _ in ll.iter() {}
+ let rll = &ll;
+ for _ in rll.iter() {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _ in vd.iter() {}
+ let rvd = &vd;
+ for _ in rvd.iter() {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _ in bh.iter() {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _ in hm.iter() {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _ in bt.iter() {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _ in hs.iter() {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _ in bs.iter() {}
+
+ struct NoIntoIter();
+ impl NoIntoIter {
+ fn iter(&self) -> slice::Iter<u8> {
+ unimplemented!()
+ }
+
+ fn iter_mut(&mut self) -> slice::IterMut<u8> {
+ unimplemented!()
+ }
+ }
+ let mut x = NoIntoIter();
+ for _ in x.iter() {} // no error
+ for _ in x.iter_mut() {} // no error
+
+ struct IntoIterDiffTy;
+ impl IntoIterator for &'_ IntoIterDiffTy {
+ type Item = &'static ();
+ type IntoIter = core::slice::Iter<'static, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl IntoIterDiffTy {
+ fn iter(&self) -> core::slice::Iter<'static, i32> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffTy;
+ for _ in x.iter() {}
+
+ struct IntoIterDiffSig;
+ impl IntoIterator for &'_ IntoIterDiffSig {
+ type Item = &'static ();
+ type IntoIter = core::slice::Iter<'static, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl IntoIterDiffSig {
+ fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffSig;
+ for _ in x.iter(0) {}
+
+ struct IntoIterDiffLt<'a>(&'a ());
+ impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
+ type Item = &'a ();
+ type IntoIter = core::slice::Iter<'a, ()>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl<'a> IntoIterDiffLt<'a> {
+ fn iter(&self) -> core::slice::Iter<'a, ()> {
+ unimplemented!()
+ }
+ }
+ let x = IntoIterDiffLt(&());
+ for _ in x.iter() {}
+
+ struct CustomType;
+ impl<'a> IntoIterator for &'a CustomType {
+ type Item = &'a u32;
+ type IntoIter = core::slice::Iter<'a, u32>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl<'a> IntoIterator for &'a mut CustomType {
+ type Item = &'a mut u32;
+ type IntoIter = core::slice::IterMut<'a, u32>;
+ fn into_iter(self) -> Self::IntoIter {
+ unimplemented!()
+ }
+ }
+ impl CustomType {
+ fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
+ panic!()
+ }
+
+ fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
+ panic!()
+ }
+ }
+ let mut x = CustomType;
+ for _ in x.iter() {}
+ for _ in x.iter_mut() {}
+
+ let r = &x;
+ for _ in r.iter() {}
+}
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr
new file mode 100644
index 000000000..94a264dce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr
@@ -0,0 +1,142 @@
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:17:14
+ |
+LL | for _ in vec.iter() {}
+ | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
+ |
+note: the lint level is defined here
+ --> $DIR/explicit_iter_loop.rs:2:9
+ |
+LL | #![deny(clippy::explicit_iter_loop)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:18:14
+ |
+LL | for _ in vec.iter_mut() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:21:14
+ |
+LL | for _ in rvec.iter() {}
+ | ^^^^^^^^^^^ help: to write this more concisely, try: `rvec`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:24:14
+ |
+LL | for _ in rmvec.iter() {}
+ | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:25:14
+ |
+LL | for _ in rmvec.iter_mut() {}
+ | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:30:14
+ |
+LL | for _ in [1, 2, 3].iter() {}
+ | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:32:14
+ |
+LL | for _ in (&mut [1, 2, 3]).iter() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])`
+
+error: the method `iter` doesn't need a mutable reference
+ --> $DIR/explicit_iter_loop.rs:32:14
+ |
+LL | for _ in (&mut [1, 2, 3]).iter() {}
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:34:14
+ |
+LL | for _ in [0; 32].iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:35:14
+ |
+LL | for _ in [0; 33].iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:38:14
+ |
+LL | for _ in ll.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:40:14
+ |
+LL | for _ in rll.iter() {}
+ | ^^^^^^^^^^ help: to write this more concisely, try: `rll`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:43:14
+ |
+LL | for _ in vd.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:45:14
+ |
+LL | for _ in rvd.iter() {}
+ | ^^^^^^^^^^ help: to write this more concisely, try: `rvd`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:48:14
+ |
+LL | for _ in bh.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:51:14
+ |
+LL | for _ in hm.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:54:14
+ |
+LL | for _ in bt.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:57:14
+ |
+LL | for _ in hs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:60:14
+ |
+LL | for _ in bs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:149:14
+ |
+LL | for _ in x.iter() {}
+ | ^^^^^^^^ help: to write this more concisely, try: `&x`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:150:14
+ |
+LL | for _ in x.iter_mut() {}
+ | ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/explicit_iter_loop.rs:153:14
+ |
+LL | for _ in r.iter() {}
+ | ^^^^^^^^ help: to write this more concisely, try: `r`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
index cdfaf8d3a..50abe89da 100644
--- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![allow(
unused,
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
index adcd1f6d4..8420df663 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
@@ -1,8 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused, clippy::needless_lifetimes)]
#![warn(clippy::extra_unused_type_parameters)]
+extern crate proc_macros;
+use proc_macros::with_span;
+
fn unused_ty(x: u8) {
unimplemented!()
}
@@ -102,4 +106,12 @@ mod issue10319 {
}
}
+with_span!(
+ span
+
+ fn should_not_lint<T>(x: u8) {
+ unimplemented!()
+ }
+);
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
index c4c5227ac..f63535d7a 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
@@ -1,8 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused, clippy::needless_lifetimes)]
#![warn(clippy::extra_unused_type_parameters)]
+extern crate proc_macros;
+use proc_macros::with_span;
+
fn unused_ty<T>(x: u8) {
unimplemented!()
}
@@ -102,4 +106,12 @@ mod issue10319 {
}
}
+with_span!(
+ span
+
+ fn should_not_lint<T>(x: u8) {
+ unimplemented!()
+ }
+);
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
index c042a5a22..b5277d498 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
@@ -1,5 +1,5 @@
error: type parameter `T` goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:6:13
+ --> $DIR/extra_unused_type_parameters.rs:10:13
|
LL | fn unused_ty<T>(x: u8) {
| ^^^ help: consider removing the parameter
@@ -7,19 +7,19 @@ LL | fn unused_ty<T>(x: u8) {
= note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
error: type parameters go unused in function definition: T, U
- --> $DIR/extra_unused_type_parameters.rs:10:16
+ --> $DIR/extra_unused_type_parameters.rs:14:16
|
LL | fn unused_multi<T, U>(x: u8) {
| ^^^^^^ help: consider removing the parameters
error: type parameter `T` goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:14:21
+ --> $DIR/extra_unused_type_parameters.rs:18:21
|
LL | fn unused_with_lt<'a, T>(x: &'a u8) {
| ^^^ help: consider removing the parameter
error: type parameters go unused in function definition: T, V
- --> $DIR/extra_unused_type_parameters.rs:26:19
+ --> $DIR/extra_unused_type_parameters.rs:30:19
|
LL | fn unused_bounded<T: Default, U, V: Default>(x: U) {
| ^^^^^^^^^^^^ ^^^^^^^^^^^^
@@ -31,7 +31,7 @@ LL + fn unused_bounded<U>(x: U) {
|
error: type parameters go unused in function definition: A, D, E
- --> $DIR/extra_unused_type_parameters.rs:30:16
+ --> $DIR/extra_unused_type_parameters.rs:34:16
|
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
| ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -43,19 +43,19 @@ LL + fn some_unused<B, C>(b: B, c: C) {
|
error: type parameter `T` goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:55:22
+ --> $DIR/extra_unused_type_parameters.rs:59:22
|
LL | fn unused_ty_impl<T>(&self) {
| ^^^ help: consider removing the parameter
error: type parameters go unused in function definition: A, B
- --> $DIR/extra_unused_type_parameters.rs:77:17
+ --> $DIR/extra_unused_type_parameters.rs:81:17
|
LL | fn unused_opaque<A, B>(dummy: impl Default) {
| ^^^^^^ help: consider removing the parameters
error: type parameter `U` goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:90:56
+ --> $DIR/extra_unused_type_parameters.rs:94:56
|
LL | fn unused_with_priv_trait_bound<T: private::Private, U>() {
| ^^^ help: consider removing the parameter
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
index 2045b1eeb..d6df114b8 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
@@ -1,5 +1,5 @@
-//@aux-build:proc_macro_derive.rs
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::field_reassign_with_default)]
diff --git a/src/tools/clippy/tests/ui/filetype_is_file.rs b/src/tools/clippy/tests/ui/filetype_is_file.rs
index 5de8fe8cd..d3ad36e40 100644
--- a/src/tools/clippy/tests/ui/filetype_is_file.rs
+++ b/src/tools/clippy/tests/ui/filetype_is_file.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::needless_if)]
#![warn(clippy::filetype_is_file)]
fn main() -> std::io::Result<()> {
diff --git a/src/tools/clippy/tests/ui/filetype_is_file.stderr b/src/tools/clippy/tests/ui/filetype_is_file.stderr
index e51a90d6c..36142deb3 100644
--- a/src/tools/clippy/tests/ui/filetype_is_file.stderr
+++ b/src/tools/clippy/tests/ui/filetype_is_file.stderr
@@ -1,5 +1,5 @@
error: `FileType::is_file()` only covers regular files
- --> $DIR/filetype_is_file.rs:8:8
+ --> $DIR/filetype_is_file.rs:9:8
|
LL | if fs::metadata("foo.txt")?.file_type().is_file() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | if fs::metadata("foo.txt")?.file_type().is_file() {
= note: `-D clippy::filetype-is-file` implied by `-D warnings`
error: `!FileType::is_file()` only denies regular files
- --> $DIR/filetype_is_file.rs:13:8
+ --> $DIR/filetype_is_file.rs:14:8
|
LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
= help: use `FileType::is_dir()` instead
error: `FileType::is_file()` only covers regular files
- --> $DIR/filetype_is_file.rs:18:9
+ --> $DIR/filetype_is_file.rs:19:9
|
LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs
index 88d3b0e74..bbd395d50 100644
--- a/src/tools/clippy/tests/ui/find_map.rs
+++ b/src/tools/clippy/tests/ui/find_map.rs
@@ -1,4 +1,5 @@
#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::useless_vec)]
#[derive(Debug, Copy, Clone)]
enum Flavor {
diff --git a/src/tools/clippy/tests/ui/fn_null_check.rs b/src/tools/clippy/tests/ui/fn_null_check.rs
index df5bc8420..dfdea100c 100644
--- a/src/tools/clippy/tests/ui/fn_null_check.rs
+++ b/src/tools/clippy/tests/ui/fn_null_check.rs
@@ -1,6 +1,7 @@
#![allow(unused)]
#![warn(clippy::fn_null_check)]
#![allow(clippy::cmp_null)]
+#![allow(clippy::needless_if)]
#![allow(clippy::ptr_eq)]
#![allow(clippy::zero_ptr)]
diff --git a/src/tools/clippy/tests/ui/fn_null_check.stderr b/src/tools/clippy/tests/ui/fn_null_check.stderr
index 660dd3239..5b9f48a96 100644
--- a/src/tools/clippy/tests/ui/fn_null_check.stderr
+++ b/src/tools/clippy/tests/ui/fn_null_check.stderr
@@ -1,5 +1,5 @@
error: function pointer assumed to be nullable, even though it isn't
- --> $DIR/fn_null_check.rs:13:8
+ --> $DIR/fn_null_check.rs:14:8
|
LL | if (fn_ptr as *mut ()).is_null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | if (fn_ptr as *mut ()).is_null() {}
= note: `-D clippy::fn-null-check` implied by `-D warnings`
error: function pointer assumed to be nullable, even though it isn't
- --> $DIR/fn_null_check.rs:14:8
+ --> $DIR/fn_null_check.rs:15:8
|
LL | if (fn_ptr as *const u8).is_null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | if (fn_ptr as *const u8).is_null() {}
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
- --> $DIR/fn_null_check.rs:15:8
+ --> $DIR/fn_null_check.rs:16:8
|
LL | if (fn_ptr as *const ()) == std::ptr::null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {}
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
- --> $DIR/fn_null_check.rs:16:8
+ --> $DIR/fn_null_check.rs:17:8
|
LL | if (fn_ptr as *const ()) == (0 as *const ()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {}
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
- --> $DIR/fn_null_check.rs:17:8
+ --> $DIR/fn_null_check.rs:18:8
|
LL | if (fn_ptr as *const ()) == ZPTR {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.fixed b/src/tools/clippy/tests/ui/for_loop_fixable.fixed
deleted file mode 100644
index f578c98da..000000000
--- a/src/tools/clippy/tests/ui/for_loop_fixable.fixed
+++ /dev/null
@@ -1,309 +0,0 @@
-//@run-rustfix
-#![allow(dead_code, unused)]
-#![allow(clippy::uninlined_format_args)]
-
-use std::collections::*;
-
-#[warn(clippy::all)]
-struct Unrelated(Vec<u8>);
-impl Unrelated {
- fn next(&self) -> std::slice::Iter<u8> {
- self.0.iter()
- }
-
- fn iter(&self) -> std::slice::Iter<u8> {
- self.0.iter()
- }
-}
-
-#[warn(
- clippy::needless_range_loop,
- clippy::explicit_iter_loop,
- clippy::explicit_into_iter_loop,
- clippy::iter_next_loop,
- clippy::for_kv_map
-)]
-#[allow(
- clippy::linkedlist,
- clippy::unnecessary_mut_passed,
- clippy::similar_names,
- clippy::needless_borrow
-)]
-#[allow(unused_variables)]
-fn main() {
- let mut vec = vec![1, 2, 3, 4];
-
- // See #601
- for i in 0..10 {
- // no error, id_col does not exist outside the loop
- let mut id_col = vec![0f64; 10];
- id_col[i] = 1f64;
- }
-
- for _v in &vec {}
-
- for _v in &mut vec {}
-
- let out_vec = vec![1, 2, 3];
- for _v in out_vec {}
-
- for _v in &vec {} // these are fine
- for _v in &mut vec {} // these are fine
-
- for _v in &[1, 2, 3] {}
-
- for _v in (&mut [1, 2, 3]).iter() {} // no error
-
- for _v in &[0; 32] {}
-
- for _v in [0; 33].iter() {} // no error
-
- let ll: LinkedList<()> = LinkedList::new();
- for _v in &ll {}
-
- let vd: VecDeque<()> = VecDeque::new();
- for _v in &vd {}
-
- let bh: BinaryHeap<()> = BinaryHeap::new();
- for _v in &bh {}
-
- let hm: HashMap<(), ()> = HashMap::new();
- for _v in &hm {}
-
- let bt: BTreeMap<(), ()> = BTreeMap::new();
- for _v in &bt {}
-
- let hs: HashSet<()> = HashSet::new();
- for _v in &hs {}
-
- let bs: BTreeSet<()> = BTreeSet::new();
- for _v in &bs {}
-
- let u = Unrelated(vec![]);
- for _v in u.next() {} // no error
- for _v in u.iter() {} // no error
-
- let mut out = vec![];
- vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
- let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
-
- // Loop with explicit counter variable
-
- // Potential false positives
- let mut _index = 0;
- _index = 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- _index += 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- if true {
- _index = 1
- }
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- let mut _index = 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index += 1;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index *= 2;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index = 1;
- _index += 1
- }
-
- let mut _index = 0;
-
- for _v in &vec {
- let mut _index = 0;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index += 1;
- _index = 0;
- }
-
- let mut _index = 0;
- for _v in &vec {
- for _x in 0..1 {
- _index += 1;
- }
- _index += 1
- }
-
- let mut _index = 0;
- for x in &vec {
- if *x == 1 {
- _index += 1
- }
- }
-
- let mut _index = 0;
- if true {
- _index = 1
- };
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 1;
- if false {
- _index = 0
- };
- for _v in &vec {
- _index += 1
- }
-
- let mut index = 0;
- {
- let mut _x = &mut index;
- }
- for _v in &vec {
- _index += 1
- }
-
- let mut index = 0;
- for _v in &vec {
- index += 1
- }
- println!("index: {}", index);
-
- fn f<T>(_: &T, _: &T) -> bool {
- unimplemented!()
- }
- fn g<T>(_: &mut [T], _: usize, _: usize) {
- unimplemented!()
- }
- for i in 1..vec.len() {
- if f(&vec[i - 1], &vec[i]) {
- g(&mut vec, i - 1, i);
- }
- }
-
- for mid in 1..vec.len() {
- let (_, _) = vec.split_at(mid);
- }
-}
-
-fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
- let pivot = v.len() - 1;
- let mut i = 0;
- for j in 0..pivot {
- if v[j] <= v[pivot] {
- v.swap(i, j);
- i += 1;
- }
- }
- v.swap(i, pivot);
- i
-}
-
-#[warn(clippy::needless_range_loop)]
-pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
- // Same source and destination - don't trigger lint
- for i in 0..dst.len() {
- dst[d + i] = dst[s + i];
- }
-}
-
-mod issue_2496 {
- pub trait Handle {
- fn new_for_index(index: usize) -> Self;
- fn index(&self) -> usize;
- }
-
- pub fn test<H: Handle>() -> H {
- for x in 0..5 {
- let next_handle = H::new_for_index(x);
- println!("{}", next_handle.index());
- }
- unimplemented!()
- }
-}
-
-// explicit_into_iter_loop bad suggestions
-#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
-mod issue_4958 {
- fn takes_iterator<T>(iterator: &T)
- where
- for<'a> &'a T: IntoIterator<Item = &'a String>,
- {
- for i in iterator {
- println!("{}", i);
- }
- }
-
- struct T;
- impl IntoIterator for &T {
- type Item = ();
- type IntoIter = std::vec::IntoIter<Self::Item>;
- fn into_iter(self) -> Self::IntoIter {
- vec![].into_iter()
- }
- }
-
- fn more_tests() {
- let t = T;
- let r = &t;
- let rr = &&t;
-
- // This case is handled by `explicit_iter_loop`. No idea why.
- for _ in &t {}
-
- for _ in r {}
-
- // No suggestion for this.
- // We'd have to suggest `for _ in *rr {}` which is less clear.
- for _ in rr.into_iter() {}
- }
-}
-
-// explicit_into_iter_loop
-#[warn(clippy::explicit_into_iter_loop)]
-mod issue_6900 {
- struct S;
- impl S {
- #[allow(clippy::should_implement_trait)]
- pub fn into_iter<T>(self) -> I<T> {
- unimplemented!()
- }
- }
-
- struct I<T>(T);
- impl<T> Iterator for I<T> {
- type Item = T;
- fn next(&mut self) -> Option<Self::Item> {
- unimplemented!()
- }
- }
-
- fn f() {
- for _ in S.into_iter::<u32>() {
- unimplemented!()
- }
- }
-}
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.rs b/src/tools/clippy/tests/ui/for_loop_fixable.rs
deleted file mode 100644
index 42bc6de0c..000000000
--- a/src/tools/clippy/tests/ui/for_loop_fixable.rs
+++ /dev/null
@@ -1,309 +0,0 @@
-//@run-rustfix
-#![allow(dead_code, unused)]
-#![allow(clippy::uninlined_format_args)]
-
-use std::collections::*;
-
-#[warn(clippy::all)]
-struct Unrelated(Vec<u8>);
-impl Unrelated {
- fn next(&self) -> std::slice::Iter<u8> {
- self.0.iter()
- }
-
- fn iter(&self) -> std::slice::Iter<u8> {
- self.0.iter()
- }
-}
-
-#[warn(
- clippy::needless_range_loop,
- clippy::explicit_iter_loop,
- clippy::explicit_into_iter_loop,
- clippy::iter_next_loop,
- clippy::for_kv_map
-)]
-#[allow(
- clippy::linkedlist,
- clippy::unnecessary_mut_passed,
- clippy::similar_names,
- clippy::needless_borrow
-)]
-#[allow(unused_variables)]
-fn main() {
- let mut vec = vec![1, 2, 3, 4];
-
- // See #601
- for i in 0..10 {
- // no error, id_col does not exist outside the loop
- let mut id_col = vec![0f64; 10];
- id_col[i] = 1f64;
- }
-
- for _v in vec.iter() {}
-
- for _v in vec.iter_mut() {}
-
- let out_vec = vec![1, 2, 3];
- for _v in out_vec.into_iter() {}
-
- for _v in &vec {} // these are fine
- for _v in &mut vec {} // these are fine
-
- for _v in [1, 2, 3].iter() {}
-
- for _v in (&mut [1, 2, 3]).iter() {} // no error
-
- for _v in [0; 32].iter() {}
-
- for _v in [0; 33].iter() {} // no error
-
- let ll: LinkedList<()> = LinkedList::new();
- for _v in ll.iter() {}
-
- let vd: VecDeque<()> = VecDeque::new();
- for _v in vd.iter() {}
-
- let bh: BinaryHeap<()> = BinaryHeap::new();
- for _v in bh.iter() {}
-
- let hm: HashMap<(), ()> = HashMap::new();
- for _v in hm.iter() {}
-
- let bt: BTreeMap<(), ()> = BTreeMap::new();
- for _v in bt.iter() {}
-
- let hs: HashSet<()> = HashSet::new();
- for _v in hs.iter() {}
-
- let bs: BTreeSet<()> = BTreeSet::new();
- for _v in bs.iter() {}
-
- let u = Unrelated(vec![]);
- for _v in u.next() {} // no error
- for _v in u.iter() {} // no error
-
- let mut out = vec![];
- vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
- let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
-
- // Loop with explicit counter variable
-
- // Potential false positives
- let mut _index = 0;
- _index = 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- _index += 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- if true {
- _index = 1
- }
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- let mut _index = 1;
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index += 1;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index *= 2;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index = 1;
- _index += 1
- }
-
- let mut _index = 0;
-
- for _v in &vec {
- let mut _index = 0;
- _index += 1
- }
-
- let mut _index = 0;
- for _v in &vec {
- _index += 1;
- _index = 0;
- }
-
- let mut _index = 0;
- for _v in &vec {
- for _x in 0..1 {
- _index += 1;
- }
- _index += 1
- }
-
- let mut _index = 0;
- for x in &vec {
- if *x == 1 {
- _index += 1
- }
- }
-
- let mut _index = 0;
- if true {
- _index = 1
- };
- for _v in &vec {
- _index += 1
- }
-
- let mut _index = 1;
- if false {
- _index = 0
- };
- for _v in &vec {
- _index += 1
- }
-
- let mut index = 0;
- {
- let mut _x = &mut index;
- }
- for _v in &vec {
- _index += 1
- }
-
- let mut index = 0;
- for _v in &vec {
- index += 1
- }
- println!("index: {}", index);
-
- fn f<T>(_: &T, _: &T) -> bool {
- unimplemented!()
- }
- fn g<T>(_: &mut [T], _: usize, _: usize) {
- unimplemented!()
- }
- for i in 1..vec.len() {
- if f(&vec[i - 1], &vec[i]) {
- g(&mut vec, i - 1, i);
- }
- }
-
- for mid in 1..vec.len() {
- let (_, _) = vec.split_at(mid);
- }
-}
-
-fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
- let pivot = v.len() - 1;
- let mut i = 0;
- for j in 0..pivot {
- if v[j] <= v[pivot] {
- v.swap(i, j);
- i += 1;
- }
- }
- v.swap(i, pivot);
- i
-}
-
-#[warn(clippy::needless_range_loop)]
-pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
- // Same source and destination - don't trigger lint
- for i in 0..dst.len() {
- dst[d + i] = dst[s + i];
- }
-}
-
-mod issue_2496 {
- pub trait Handle {
- fn new_for_index(index: usize) -> Self;
- fn index(&self) -> usize;
- }
-
- pub fn test<H: Handle>() -> H {
- for x in 0..5 {
- let next_handle = H::new_for_index(x);
- println!("{}", next_handle.index());
- }
- unimplemented!()
- }
-}
-
-// explicit_into_iter_loop bad suggestions
-#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
-mod issue_4958 {
- fn takes_iterator<T>(iterator: &T)
- where
- for<'a> &'a T: IntoIterator<Item = &'a String>,
- {
- for i in iterator.into_iter() {
- println!("{}", i);
- }
- }
-
- struct T;
- impl IntoIterator for &T {
- type Item = ();
- type IntoIter = std::vec::IntoIter<Self::Item>;
- fn into_iter(self) -> Self::IntoIter {
- vec![].into_iter()
- }
- }
-
- fn more_tests() {
- let t = T;
- let r = &t;
- let rr = &&t;
-
- // This case is handled by `explicit_iter_loop`. No idea why.
- for _ in t.into_iter() {}
-
- for _ in r.into_iter() {}
-
- // No suggestion for this.
- // We'd have to suggest `for _ in *rr {}` which is less clear.
- for _ in rr.into_iter() {}
- }
-}
-
-// explicit_into_iter_loop
-#[warn(clippy::explicit_into_iter_loop)]
-mod issue_6900 {
- struct S;
- impl S {
- #[allow(clippy::should_implement_trait)]
- pub fn into_iter<T>(self) -> I<T> {
- unimplemented!()
- }
- }
-
- struct I<T>(T);
- impl<T> Iterator for I<T> {
- type Item = T;
- fn next(&mut self) -> Option<Self::Item> {
- unimplemented!()
- }
- }
-
- fn f() {
- for _ in S.into_iter::<u32>() {
- unimplemented!()
- }
- }
-}
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.stderr b/src/tools/clippy/tests/ui/for_loop_fixable.stderr
deleted file mode 100644
index ddfe66d67..000000000
--- a/src/tools/clippy/tests/ui/for_loop_fixable.stderr
+++ /dev/null
@@ -1,96 +0,0 @@
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:43:15
- |
-LL | for _v in vec.iter() {}
- | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
- |
- = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:45:15
- |
-LL | for _v in vec.iter_mut() {}
- | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
-
-error: it is more concise to loop over containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:48:15
- |
-LL | for _v in out_vec.into_iter() {}
- | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
- |
- = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:53:15
- |
-LL | for _v in [1, 2, 3].iter() {}
- | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:57:15
- |
-LL | for _v in [0; 32].iter() {}
- | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:62:15
- |
-LL | for _v in ll.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:65:15
- |
-LL | for _v in vd.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:68:15
- |
-LL | for _v in bh.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:71:15
- |
-LL | for _v in hm.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:74:15
- |
-LL | for _v in bt.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:77:15
- |
-LL | for _v in hs.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:80:15
- |
-LL | for _v in bs.iter() {}
- | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
-
-error: it is more concise to loop over containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:255:18
- |
-LL | for i in iterator.into_iter() {
- | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:275:18
- |
-LL | for _ in t.into_iter() {}
- | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
-
-error: it is more concise to loop over containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:277:18
- |
-LL | for _ in r.into_iter() {}
- | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
-
-error: aborting due to 15 previous errors
-
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
deleted file mode 100644
index 55fb3788a..000000000
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Tests from for_loop.rs that don't have suggestions
-
-#[warn(
- clippy::needless_range_loop,
- clippy::explicit_iter_loop,
- clippy::explicit_into_iter_loop,
- clippy::iter_next_loop,
- clippy::for_kv_map
-)]
-#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
-#[allow(for_loops_over_fallibles)]
-fn main() {
- let vec = vec![1, 2, 3, 4];
-
- for _v in vec.iter().next() {}
-}
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
deleted file mode 100644
index 50a86eaa6..000000000
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
+++ /dev/null
@@ -1,10 +0,0 @@
-error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
- --> $DIR/for_loop_unfixable.rs:15:15
- |
-LL | for _v in vec.iter().next() {}
- | ^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::iter-next-loop` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index 9288956f5..2e24e07ea 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -6,7 +6,9 @@
clippy::redundant_clone,
clippy::to_string_in_format_args,
clippy::needless_borrow,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::needless_raw_string_hashes,
+ clippy::useless_vec
)]
struct Foo(pub String);
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index b2b817e0f..0e64a310b 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -6,7 +6,9 @@
clippy::redundant_clone,
clippy::to_string_in_format_args,
clippy::needless_borrow,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::needless_raw_string_hashes,
+ clippy::useless_vec
)]
struct Foo(pub String);
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index 0ef0ac655..78a11a335 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -1,5 +1,5 @@
error: useless use of `format!`
- --> $DIR/format.rs:19:5
+ --> $DIR/format.rs:21:5
|
LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@@ -7,19 +7,19 @@ LL | format!("foo");
= note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!`
- --> $DIR/format.rs:20:5
+ --> $DIR/format.rs:22:5
|
LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:21:5
+ --> $DIR/format.rs:23:5
|
LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:22:5
+ --> $DIR/format.rs:24:5
|
LL | / format!(
LL | | r##"foo {{}}
@@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
|
error: useless use of `format!`
- --> $DIR/format.rs:27:13
+ --> $DIR/format.rs:29:13
|
LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!`
- --> $DIR/format.rs:29:5
+ --> $DIR/format.rs:31:5
|
LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:37:5
+ --> $DIR/format.rs:39:5
|
LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:67:5
+ --> $DIR/format.rs:69:5
|
LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:69:5
+ --> $DIR/format.rs:71:5
|
LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:73:18
+ --> $DIR/format.rs:75:18
|
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!`
- --> $DIR/format.rs:77:22
+ --> $DIR/format.rs:79:22
|
LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:83:13
+ --> $DIR/format.rs:85:13
|
LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:85:13
+ --> $DIR/format.rs:87:13
|
LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:89:13
+ --> $DIR/format.rs:91:13
|
LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:91:13
+ --> $DIR/format.rs:93:13
|
LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
diff --git a/src/tools/clippy/tests/ui/format_push_string.rs b/src/tools/clippy/tests/ui/format_push_string.rs
index 4db13d650..89423ffe1 100644
--- a/src/tools/clippy/tests/ui/format_push_string.rs
+++ b/src/tools/clippy/tests/ui/format_push_string.rs
@@ -5,3 +5,32 @@ fn main() {
string += &format!("{:?}", 1234);
string.push_str(&format!("{:?}", 5678));
}
+
+mod issue9493 {
+ pub fn u8vec_to_hex(vector: &Vec<u8>, upper: bool) -> String {
+ let mut hex = String::with_capacity(vector.len() * 2);
+ for byte in vector {
+ hex += &(if upper {
+ format!("{byte:02X}")
+ } else {
+ format!("{byte:02x}")
+ });
+ }
+ hex
+ }
+
+ pub fn other_cases() {
+ let mut s = String::new();
+ // if let
+ s += &(if let Some(_a) = Some(1234) {
+ format!("{}", 1234)
+ } else {
+ format!("{}", 1234)
+ });
+ // match
+ s += &(match Some(1234) {
+ Some(_) => format!("{}", 1234),
+ None => format!("{}", 1234),
+ });
+ }
+}
diff --git a/src/tools/clippy/tests/ui/format_push_string.stderr b/src/tools/clippy/tests/ui/format_push_string.stderr
index d7be9a5f2..76762c4a1 100644
--- a/src/tools/clippy/tests/ui/format_push_string.stderr
+++ b/src/tools/clippy/tests/ui/format_push_string.stderr
@@ -15,5 +15,40 @@ LL | string.push_str(&format!("{:?}", 5678));
|
= help: consider using `write!` to avoid the extra allocation
-error: aborting due to 2 previous errors
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:13:13
+ |
+LL | / hex += &(if upper {
+LL | | format!("{byte:02X}")
+LL | | } else {
+LL | | format!("{byte:02x}")
+LL | | });
+ | |______________^
+ |
+ = help: consider using `write!` to avoid the extra allocation
+
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:25:9
+ |
+LL | / s += &(if let Some(_a) = Some(1234) {
+LL | | format!("{}", 1234)
+LL | | } else {
+LL | | format!("{}", 1234)
+LL | | });
+ | |__________^
+ |
+ = help: consider using `write!` to avoid the extra allocation
+
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:31:9
+ |
+LL | / s += &(match Some(1234) {
+LL | | Some(_) => format!("{}", 1234),
+LL | | None => format!("{}", 1234),
+LL | | });
+ | |__________^
+ |
+ = help: consider using `write!` to avoid the extra allocation
+
+error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed
index 915ff4fb0..1671987cb 100644
--- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed
@@ -2,6 +2,7 @@
#![warn(clippy::from_iter_instead_of_collect)]
#![allow(unused_imports, unused_tuple_struct_fields)]
+#![allow(clippy::useless_vec)]
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs
index e926f8c52..48509b32f 100644
--- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs
@@ -2,6 +2,7 @@
#![warn(clippy::from_iter_instead_of_collect)]
#![allow(unused_imports, unused_tuple_struct_fields)]
+#![allow(clippy::useless_vec)]
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr
index 8aa3c3c01..8f08ac8c3 100644
--- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr
@@ -1,5 +1,5 @@
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:18:9
+ --> $DIR/from_iter_instead_of_collect.rs:19:9
|
LL | <Self as FromIterator<bool>>::from_iter(iter.into_iter().copied())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::<Self>()`
@@ -7,85 +7,85 @@ LL | <Self as FromIterator<bool>>::from_iter(iter.into_iter().copied())
= note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:24:13
+ --> $DIR/from_iter_instead_of_collect.rs:25:13
|
LL | let _ = Vec::from_iter(iter_expr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::<Vec<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:26:13
+ --> $DIR/from_iter_instead_of_collect.rs:27:13
|
LL | let _ = HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::<HashMap<usize, &i8>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:31:19
+ --> $DIR/from_iter_instead_of_collect.rs:32:19
|
LL | assert_eq!(a, Vec::from_iter(0..3));
| ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:32:19
+ --> $DIR/from_iter_instead_of_collect.rs:33:19
|
LL | assert_eq!(a, Vec::<i32>::from_iter(0..3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<i32>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:34:17
+ --> $DIR/from_iter_instead_of_collect.rs:35:17
|
LL | let mut b = VecDeque::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:37:17
+ --> $DIR/from_iter_instead_of_collect.rs:38:17
|
LL | let mut b = VecDeque::<i32>::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<i32>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:42:21
+ --> $DIR/from_iter_instead_of_collect.rs:43:21
|
LL | let mut b = collections::VecDeque::<i32>::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::VecDeque<i32>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:47:14
+ --> $DIR/from_iter_instead_of_collect.rs:48:14
|
LL | let bm = BTreeMap::from_iter(values.iter().cloned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::<BTreeMap<_, _>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:48:19
+ --> $DIR/from_iter_instead_of_collect.rs:49:19
|
LL | let mut bar = BTreeMap::from_iter(bm.range(0..2));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::<BTreeMap<_, _>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:51:19
+ --> $DIR/from_iter_instead_of_collect.rs:52:19
|
LL | let mut bts = BTreeSet::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<BTreeSet<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:55:17
+ --> $DIR/from_iter_instead_of_collect.rs:56:17
|
LL | let _ = collections::BTreeSet::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:56:17
+ --> $DIR/from_iter_instead_of_collect.rs:57:17
|
LL | let _ = collections::BTreeSet::<u32>::from_iter(0..3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<u32>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:59:15
+ --> $DIR/from_iter_instead_of_collect.rs:60:15
|
LL | for _i in Vec::from_iter([1, 2, 3].iter()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<_>>()`
error: usage of `FromIterator::from_iter`
- --> $DIR/from_iter_instead_of_collect.rs:60:15
+ --> $DIR/from_iter_instead_of_collect.rs:61:15
|
LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<&i32>>()`
diff --git a/src/tools/clippy/tests/ui/from_over_into.fixed b/src/tools/clippy/tests/ui/from_over_into.fixed
index d18f93875..d96b68a91 100644
--- a/src/tools/clippy/tests/ui/from_over_into.fixed
+++ b/src/tools/clippy/tests/ui/from_over_into.fixed
@@ -60,6 +60,15 @@ impl From<String> for A {
}
}
+struct PathInExpansion;
+
+impl From<PathInExpansion> for String {
+ fn from(val: PathInExpansion) -> Self {
+ // non self/Self paths in expansions are fine
+ panic!()
+ }
+}
+
#[clippy::msrv = "1.40"]
fn msrv_1_40() {
struct FromOverInto<T>(Vec<T>);
@@ -82,10 +91,4 @@ fn msrv_1_41() {
}
}
-type Opaque = impl Sized;
-struct IntoOpaque;
-impl Into<Opaque> for IntoOpaque {
- fn into(self) -> Opaque {}
-}
-
fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs
index de8ff0b06..da8fe04f4 100644
--- a/src/tools/clippy/tests/ui/from_over_into.rs
+++ b/src/tools/clippy/tests/ui/from_over_into.rs
@@ -60,6 +60,15 @@ impl From<String> for A {
}
}
+struct PathInExpansion;
+
+impl Into<String> for PathInExpansion {
+ fn into(self) -> String {
+ // non self/Self paths in expansions are fine
+ panic!()
+ }
+}
+
#[clippy::msrv = "1.40"]
fn msrv_1_40() {
struct FromOverInto<T>(Vec<T>);
@@ -82,10 +91,4 @@ fn msrv_1_41() {
}
}
-type Opaque = impl Sized;
-struct IntoOpaque;
-impl Into<Opaque> for IntoOpaque {
- fn into(self) -> Opaque {}
-}
-
fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr
index 6039f86fe..498b00de5 100644
--- a/src/tools/clippy/tests/ui/from_over_into.stderr
+++ b/src/tools/clippy/tests/ui/from_over_into.stderr
@@ -59,7 +59,21 @@ LL ~ val.0
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:78:5
+ --> $DIR/from_over_into.rs:65:1
+ |
+LL | impl Into<String> for PathInExpansion {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
+ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
+help: replace the `Into` implementation with `From<PathInExpansion>`
+ |
+LL ~ impl From<PathInExpansion> for String {
+LL ~ fn from(val: PathInExpansion) -> Self {
+ |
+
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+ --> $DIR/from_over_into.rs:87:5
|
LL | impl<T> Into<FromOverInto<T>> for Vec<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -71,5 +85,5 @@ LL ~ fn from(val: Vec<T>) -> Self {
LL ~ FromOverInto(val)
|
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui/from_over_into_unfixable.rs b/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
index 3b280b748..c769e38eb 100644
--- a/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
+++ b/src/tools/clippy/tests/ui/from_over_into_unfixable.rs
@@ -3,14 +3,14 @@
struct InMacro(String);
macro_rules! in_macro {
- ($e:ident) => {
- $e
+ () => {
+ Self::new()
};
}
impl Into<InMacro> for String {
fn into(self) -> InMacro {
- InMacro(in_macro!(self))
+ InMacro(in_macro!())
}
}
@@ -32,4 +32,14 @@ impl Into<u8> for ContainsVal {
}
}
+pub struct Lval<T>(T);
+
+pub struct Rval<T>(T);
+
+impl<T> Into<Rval<Self>> for Lval<T> {
+ fn into(self) -> Rval<Self> {
+ Rval(self)
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr b/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
index 251f1d84e..2ab9b9d6b 100644
--- a/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
@@ -25,5 +25,13 @@ LL | impl Into<u8> for ContainsVal {
https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
= help: replace the `Into` implementation with `From<ContainsVal>`
-error: aborting due to 3 previous errors
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+ --> $DIR/from_over_into_unfixable.rs:39:1
+ |
+LL | impl<T> Into<Rval<Self>> for Lval<T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: replace the `Into` implementation with `From<Lval<T>>`
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs b/src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs
index 8484da241..95ef6425f 100644
--- a/src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs
+++ b/src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs
@@ -1,4 +1,5 @@
#![warn(clippy::from_raw_with_void_ptr)]
+#![allow(clippy::unnecessary_cast)]
use std::ffi::c_void;
use std::rc::Rc;
diff --git a/src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr b/src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr
index 96e4af12b..1963d0801 100644
--- a/src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr
+++ b/src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr
@@ -1,60 +1,60 @@
error: creating a `Box` from a void raw pointer
- --> $DIR/from_raw_with_void_ptr.rs:10:22
+ --> $DIR/from_raw_with_void_ptr.rs:11:22
|
LL | let _ = unsafe { Box::from_raw(ptr) };
| ^^^^^^^^^^^^^^^^^^
|
help: cast this to a pointer of the appropriate type
- --> $DIR/from_raw_with_void_ptr.rs:10:36
+ --> $DIR/from_raw_with_void_ptr.rs:11:36
|
LL | let _ = unsafe { Box::from_raw(ptr) };
| ^^^
= note: `-D clippy::from-raw-with-void-ptr` implied by `-D warnings`
error: creating a `Rc` from a void raw pointer
- --> $DIR/from_raw_with_void_ptr.rs:21:22
+ --> $DIR/from_raw_with_void_ptr.rs:22:22
|
LL | let _ = unsafe { Rc::from_raw(ptr) };
| ^^^^^^^^^^^^^^^^^
|
help: cast this to a pointer of the appropriate type
- --> $DIR/from_raw_with_void_ptr.rs:21:35
+ --> $DIR/from_raw_with_void_ptr.rs:22:35
|
LL | let _ = unsafe { Rc::from_raw(ptr) };
| ^^^
error: creating a `Arc` from a void raw pointer
- --> $DIR/from_raw_with_void_ptr.rs:25:22
+ --> $DIR/from_raw_with_void_ptr.rs:26:22
|
LL | let _ = unsafe { Arc::from_raw(ptr) };
| ^^^^^^^^^^^^^^^^^^
|
help: cast this to a pointer of the appropriate type
- --> $DIR/from_raw_with_void_ptr.rs:25:36
+ --> $DIR/from_raw_with_void_ptr.rs:26:36
|
LL | let _ = unsafe { Arc::from_raw(ptr) };
| ^^^
error: creating a `Weak` from a void raw pointer
- --> $DIR/from_raw_with_void_ptr.rs:29:22
+ --> $DIR/from_raw_with_void_ptr.rs:30:22
|
LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: cast this to a pointer of the appropriate type
- --> $DIR/from_raw_with_void_ptr.rs:29:46
+ --> $DIR/from_raw_with_void_ptr.rs:30:46
|
LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) };
| ^^^
error: creating a `Weak` from a void raw pointer
- --> $DIR/from_raw_with_void_ptr.rs:33:22
+ --> $DIR/from_raw_with_void_ptr.rs:34:22
|
LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: cast this to a pointer of the appropriate type
- --> $DIR/from_raw_with_void_ptr.rs:33:48
+ --> $DIR/from_raw_with_void_ptr.rs:34:48
|
LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) };
| ^^^
diff --git a/src/tools/clippy/tests/ui/get_first.fixed b/src/tools/clippy/tests/ui/get_first.fixed
index ef132b796..a29c0918a 100644
--- a/src/tools/clippy/tests/ui/get_first.fixed
+++ b/src/tools/clippy/tests/ui/get_first.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::get_first)]
+#![allow(clippy::useless_vec)]
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/get_first.rs b/src/tools/clippy/tests/ui/get_first.rs
index 4d8722356..2062f3ec2 100644
--- a/src/tools/clippy/tests/ui/get_first.rs
+++ b/src/tools/clippy/tests/ui/get_first.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::get_first)]
+#![allow(clippy::useless_vec)]
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/get_first.stderr b/src/tools/clippy/tests/ui/get_first.stderr
index 466beff9c..4e267ba9a 100644
--- a/src/tools/clippy/tests/ui/get_first.stderr
+++ b/src/tools/clippy/tests/ui/get_first.stderr
@@ -1,5 +1,5 @@
error: accessing first element with `x.get(0)`
- --> $DIR/get_first.rs:19:13
+ --> $DIR/get_first.rs:20:13
|
LL | let _ = x.get(0); // Use x.first()
| ^^^^^^^^ help: try: `x.first()`
@@ -7,13 +7,13 @@ LL | let _ = x.get(0); // Use x.first()
= note: `-D clippy::get-first` implied by `-D warnings`
error: accessing first element with `y.get(0)`
- --> $DIR/get_first.rs:24:13
+ --> $DIR/get_first.rs:25:13
|
LL | let _ = y.get(0); // Use y.first()
| ^^^^^^^^ help: try: `y.first()`
error: accessing first element with `z.get(0)`
- --> $DIR/get_first.rs:29:13
+ --> $DIR/get_first.rs:30:13
|
LL | let _ = z.get(0); // Use z.first()
| ^^^^^^^^ help: try: `z.first()`
diff --git a/src/tools/clippy/tests/ui/get_last_with_len.fixed b/src/tools/clippy/tests/ui/get_last_with_len.fixed
index a58dfda79..01a83e5bf 100644
--- a/src/tools/clippy/tests/ui/get_last_with_len.fixed
+++ b/src/tools/clippy/tests/ui/get_last_with_len.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::get_last_with_len)]
-#![allow(unused)]
+#![allow(unused, clippy::useless_vec)]
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/get_last_with_len.rs b/src/tools/clippy/tests/ui/get_last_with_len.rs
index d626656c7..d82484b46 100644
--- a/src/tools/clippy/tests/ui/get_last_with_len.rs
+++ b/src/tools/clippy/tests/ui/get_last_with_len.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::get_last_with_len)]
-#![allow(unused)]
+#![allow(unused, clippy::useless_vec)]
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed
index 4950c47dd..56ee37f02 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.fixed
+++ b/src/tools/clippy/tests/ui/get_unwrap.fixed
@@ -1,6 +1,11 @@
//@run-rustfix
-#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
+#![allow(
+ unused_mut,
+ clippy::from_iter_instead_of_collect,
+ clippy::get_first,
+ clippy::useless_vec
+)]
#![warn(clippy::unwrap_used)]
#![deny(clippy::get_unwrap)]
@@ -65,3 +70,42 @@ fn main() {
let _ = some_vec[0..1].to_vec();
}
}
+mod issue9909 {
+ #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
+
+ fn reduced() {
+ let f = &[1, 2, 3];
+
+ // include a borrow in the suggestion, even if the argument is not just a numeric literal
+ let _x: &i32 = &f[1 + 2];
+
+ // don't include a borrow here
+ let _x = f[1 + 2].to_string();
+
+ // don't include a borrow here
+ let _x = f[1 + 2].abs();
+ }
+
+ // original code:
+ fn linidx(row: usize, col: usize) -> usize {
+ row * 1 + col * 3
+ }
+
+ fn main_() {
+ let mut mat = [1.0f32, 5.0, 9.0, 2.0, 6.0, 10.0, 3.0, 7.0, 11.0, 4.0, 8.0, 12.0];
+
+ for i in 0..2 {
+ for j in i + 1..3 {
+ if mat[linidx(j, 3)] > mat[linidx(i, 3)] {
+ for k in 0..4 {
+ let (x, rest) = mat.split_at_mut(linidx(i, k) + 1);
+ let a = x.last_mut().unwrap();
+ let b = &mut rest[linidx(j, k) - linidx(i, k) - 1];
+ ::std::mem::swap(a, b);
+ }
+ }
+ }
+ }
+ assert_eq!([9.0, 5.0, 1.0, 10.0, 6.0, 2.0, 11.0, 7.0, 3.0, 12.0, 8.0, 4.0], mat);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs
index 6b1e8edb7..af3a619ad 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.rs
+++ b/src/tools/clippy/tests/ui/get_unwrap.rs
@@ -1,6 +1,11 @@
//@run-rustfix
-#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
+#![allow(
+ unused_mut,
+ clippy::from_iter_instead_of_collect,
+ clippy::get_first,
+ clippy::useless_vec
+)]
#![warn(clippy::unwrap_used)]
#![deny(clippy::get_unwrap)]
@@ -65,3 +70,42 @@ fn main() {
let _ = some_vec.get_mut(0..1).unwrap().to_vec();
}
}
+mod issue9909 {
+ #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
+
+ fn reduced() {
+ let f = &[1, 2, 3];
+
+ // include a borrow in the suggestion, even if the argument is not just a numeric literal
+ let _x: &i32 = f.get(1 + 2).unwrap();
+
+ // don't include a borrow here
+ let _x = f.get(1 + 2).unwrap().to_string();
+
+ // don't include a borrow here
+ let _x = f.get(1 + 2).unwrap().abs();
+ }
+
+ // original code:
+ fn linidx(row: usize, col: usize) -> usize {
+ row * 1 + col * 3
+ }
+
+ fn main_() {
+ let mut mat = [1.0f32, 5.0, 9.0, 2.0, 6.0, 10.0, 3.0, 7.0, 11.0, 4.0, 8.0, 12.0];
+
+ for i in 0..2 {
+ for j in i + 1..3 {
+ if mat[linidx(j, 3)] > mat[linidx(i, 3)] {
+ for k in 0..4 {
+ let (x, rest) = mat.split_at_mut(linidx(i, k) + 1);
+ let a = x.last_mut().unwrap();
+ let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
+ ::std::mem::swap(a, b);
+ }
+ }
+ }
+ }
+ assert_eq!([9.0, 5.0, 1.0, 10.0, 6.0, 2.0, 11.0, 7.0, 3.0, 12.0, 8.0, 4.0], mat);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr
index 6dee4d5b4..fd961420d 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/get_unwrap.stderr
@@ -1,17 +1,17 @@
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:35:17
+ --> $DIR/get_unwrap.rs:40:17
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
|
note: the lint level is defined here
- --> $DIR/get_unwrap.rs:5:9
+ --> $DIR/get_unwrap.rs:10:9
|
LL | #![deny(clippy::get_unwrap)]
| ^^^^^^^^^^^^^^^^^^
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:35:17
+ --> $DIR/get_unwrap.rs:40:17
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap();
= note: `-D clippy::unwrap-used` implied by `-D warnings`
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:36:17
+ --> $DIR/get_unwrap.rs:41:17
|
LL | let _ = some_slice.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:36:17
+ --> $DIR/get_unwrap.rs:41:17
|
LL | let _ = some_slice.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:37:17
+ --> $DIR/get_unwrap.rs:42:17
|
LL | let _ = some_vec.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:37:17
+ --> $DIR/get_unwrap.rs:42:17
|
LL | let _ = some_vec.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:38:17
+ --> $DIR/get_unwrap.rs:43:17
|
LL | let _ = some_vecdeque.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:38:17
+ --> $DIR/get_unwrap.rs:43:17
|
LL | let _ = some_vecdeque.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:39:17
+ --> $DIR/get_unwrap.rs:44:17
|
LL | let _ = some_hashmap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:39:17
+ --> $DIR/get_unwrap.rs:44:17
|
LL | let _ = some_hashmap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:40:17
+ --> $DIR/get_unwrap.rs:45:17
|
LL | let _ = some_btreemap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:40:17
+ --> $DIR/get_unwrap.rs:45:17
|
LL | let _ = some_btreemap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:44:21
+ --> $DIR/get_unwrap.rs:49:21
|
LL | let _: u8 = *boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:44:22
+ --> $DIR/get_unwrap.rs:49:22
|
LL | let _: u8 = *boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:49:9
+ --> $DIR/get_unwrap.rs:54:9
|
LL | *boxed_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:49:10
+ --> $DIR/get_unwrap.rs:54:10
|
LL | *boxed_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:50:9
+ --> $DIR/get_unwrap.rs:55:9
|
LL | *some_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:50:10
+ --> $DIR/get_unwrap.rs:55:10
|
LL | *some_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:51:9
+ --> $DIR/get_unwrap.rs:56:9
|
LL | *some_vec.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:51:10
+ --> $DIR/get_unwrap.rs:56:10
|
LL | *some_vec.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:52:9
+ --> $DIR/get_unwrap.rs:57:9
|
LL | *some_vecdeque.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:52:10
+ --> $DIR/get_unwrap.rs:57:10
|
LL | *some_vecdeque.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1;
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:64:17
+ --> $DIR/get_unwrap.rs:69:17
|
LL | let _ = some_vec.get(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:64:17
+ --> $DIR/get_unwrap.rs:69:17
|
LL | let _ = some_vec.get(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -174,18 +174,42 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:65:17
+ --> $DIR/get_unwrap.rs:70:17
|
LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
error: used `unwrap()` on an `Option` value
- --> $DIR/get_unwrap.rs:65:17
+ --> $DIR/get_unwrap.rs:70:17
|
LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
-error: aborting due to 26 previous errors
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:80:24
+ |
+LL | let _x: &i32 = f.get(1 + 2).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `&f[1 + 2]`
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:83:18
+ |
+LL | let _x = f.get(1 + 2).unwrap().to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `f[1 + 2]`
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:86:18
+ |
+LL | let _x = f.get(1 + 2).unwrap().abs();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `f[1 + 2]`
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:103:33
+ |
+LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&mut rest[linidx(j, k) - linidx(i, k) - 1]`
+
+error: aborting due to 30 previous errors
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs
index 07d2002eb..dad4543f8 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else.rs
+++ b/src/tools/clippy/tests/ui/if_same_then_else.rs
@@ -21,6 +21,7 @@ fn foo() -> bool {
fn if_same_then_else() {
if true {
+ //~^ ERROR: this `if` has identical blocks
Foo { bar: 42 };
0..10;
..;
@@ -29,7 +30,6 @@ fn if_same_then_else() {
0..=10;
foo();
} else {
- //~ ERROR same body as `if` block
Foo { bar: 42 };
0..10;
..;
@@ -65,16 +65,16 @@ fn if_same_then_else() {
}
let _ = if true {
+ //~^ ERROR: this `if` has identical blocks
0.0
} else {
- //~ ERROR same body as `if` block
0.0
};
let _ = if true {
+ //~^ ERROR: this `if` has identical blocks
-0.0
} else {
- //~ ERROR same body as `if` block
-0.0
};
@@ -88,13 +88,14 @@ fn if_same_then_else() {
}
let _ = if true {
+ //~^ ERROR: this `if` has identical blocks
42
} else {
- //~ ERROR same body as `if` block
42
};
if true {
+ //~^ ERROR: this `if` has identical blocks
let bar = if true { 42 } else { 43 };
while foo() {
@@ -102,7 +103,6 @@ fn if_same_then_else() {
}
bar + 1;
} else {
- //~ ERROR same body as `if` block
let bar = if true { 42 } else { 43 };
while foo() {
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr
index fb23b81d3..a34fc5655 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else.stderr
+++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr
@@ -3,22 +3,22 @@ error: this `if` has identical blocks
|
LL | if true {
| _____________^
+LL | |
LL | | Foo { bar: 42 };
LL | | 0..10;
-LL | | ..;
... |
LL | | foo();
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else.rs:31:12
+ --> $DIR/if_same_then_else.rs:32:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | Foo { bar: 42 };
LL | | 0..10;
+LL | | ..;
... |
LL | | foo();
LL | | }
@@ -30,16 +30,16 @@ error: this `if` has identical blocks
|
LL | let _ = if true {
| _____________________^
+LL | |
LL | | 0.0
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else.rs:69:12
+ --> $DIR/if_same_then_else.rs:70:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | 0.0
LL | | };
| |_____^
@@ -49,16 +49,16 @@ error: this `if` has identical blocks
|
LL | let _ = if true {
| _____________________^
+LL | |
LL | | -0.0
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else.rs:76:12
+ --> $DIR/if_same_then_else.rs:77:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | -0.0
LL | | };
| |_____^
@@ -68,16 +68,16 @@ error: this `if` has identical blocks
|
LL | let _ = if true {
| _____________________^
+LL | |
LL | | 42
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else.rs:92:12
+ --> $DIR/if_same_then_else.rs:93:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | 42
LL | | };
| |_____^
@@ -87,22 +87,22 @@ error: this `if` has identical blocks
|
LL | if true {
| _____________^
+LL | |
LL | | let bar = if true { 42 } else { 43 };
LL | |
-LL | | while foo() {
... |
LL | | bar + 1;
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else.rs:104:12
+ --> $DIR/if_same_then_else.rs:105:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | let bar = if true { 42 } else { 43 };
LL | |
+LL | | while foo() {
... |
LL | | bar + 1;
LL | | }
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs
index 58167f444..0b171f21d 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else2.rs
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs
@@ -5,6 +5,7 @@
clippy::equatable_if_let,
clippy::collapsible_if,
clippy::ifs_same_cond,
+ clippy::needless_if,
clippy::needless_return,
clippy::single_element_loop,
clippy::branches_sharing_code
@@ -12,6 +13,7 @@
fn if_same_then_else2() -> Result<&'static str, ()> {
if true {
+ //~^ ERROR: this `if` has identical blocks
for _ in &[42] {
let foo: &Option<_> = &Some::<u8>(42);
if foo.is_some() {
@@ -21,7 +23,6 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
}
}
} else {
- //~ ERROR same body as `if` block
for _ in &[42] {
let bar: &Option<_> = &Some::<u8>(42);
if bar.is_some() {
@@ -33,16 +34,16 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
}
if true {
+ //~^ ERROR: this `if` has identical blocks
if let Some(a) = Some(42) {}
} else {
- //~ ERROR same body as `if` block
if let Some(a) = Some(42) {}
}
if true {
+ //~^ ERROR: this `if` has identical blocks
if let (1, .., 3) = (1, 2, 3) {}
} else {
- //~ ERROR same body as `if` block
if let (1, .., 3) = (1, 2, 3) {}
}
@@ -90,16 +91,16 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
// Same NaNs
let _ = if true {
+ //~^ ERROR: this `if` has identical blocks
f32::NAN
} else {
- //~ ERROR same body as `if` block
f32::NAN
};
if true {
+ //~^ ERROR: this `if` has identical blocks
Ok("foo")?;
} else {
- //~ ERROR same body as `if` block
Ok("foo")?;
}
@@ -121,6 +122,7 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
let foo = "bar";
return Ok(&foo[0..]);
} else if true {
+ //~^ ERROR: this `if` has identical blocks
let foo = "";
return Ok(&foo[0..]);
} else {
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
index 704cfd966..56e5f3e45 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
@@ -1,24 +1,24 @@
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:14:13
+ --> $DIR/if_same_then_else2.rs:15:13
|
LL | if true {
| _____________^
+LL | |
LL | | for _ in &[42] {
LL | | let foo: &Option<_> = &Some::<u8>(42);
-LL | | if foo.is_some() {
... |
LL | | }
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:23:12
+ --> $DIR/if_same_then_else2.rs:25:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | for _ in &[42] {
LL | | let bar: &Option<_> = &Some::<u8>(42);
+LL | | if bar.is_some() {
... |
LL | | }
LL | | }
@@ -26,93 +26,94 @@ LL | | }
= note: `-D clippy::if-same-then-else` implied by `-D warnings`
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:35:13
+ --> $DIR/if_same_then_else2.rs:36:13
|
LL | if true {
| _____________^
+LL | |
LL | | if let Some(a) = Some(42) {}
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:37:12
+ --> $DIR/if_same_then_else2.rs:39:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | if let Some(a) = Some(42) {}
LL | | }
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:42:13
+ --> $DIR/if_same_then_else2.rs:43:13
|
LL | if true {
| _____________^
+LL | |
LL | | if let (1, .., 3) = (1, 2, 3) {}
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:44:12
+ --> $DIR/if_same_then_else2.rs:46:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | if let (1, .., 3) = (1, 2, 3) {}
LL | | }
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:92:21
+ --> $DIR/if_same_then_else2.rs:93:21
|
LL | let _ = if true {
| _____________________^
+LL | |
LL | | f32::NAN
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:94:12
+ --> $DIR/if_same_then_else2.rs:96:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | f32::NAN
LL | | };
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:99:13
+ --> $DIR/if_same_then_else2.rs:100:13
|
LL | if true {
| _____________^
+LL | |
LL | | Ok("foo")?;
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:101:12
+ --> $DIR/if_same_then_else2.rs:103:12
|
LL | } else {
| ____________^
-LL | | //~ ERROR same body as `if` block
LL | | Ok("foo")?;
LL | | }
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:123:20
+ --> $DIR/if_same_then_else2.rs:124:20
|
LL | } else if true {
| ____________________^
+LL | |
LL | | let foo = "";
LL | | return Ok(&foo[0..]);
LL | | } else {
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:126:12
+ --> $DIR/if_same_then_else2.rs:128:12
|
LL | } else {
| ____________^
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs
index 9ce9a8762..5c338e3c5 100644
--- a/src/tools/clippy/tests/ui/ifs_same_cond.rs
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs
@@ -1,5 +1,10 @@
#![warn(clippy::ifs_same_cond)]
-#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
+#![allow(
+ clippy::if_same_then_else,
+ clippy::comparison_chain,
+ clippy::needless_if,
+ clippy::needless_else
+)] // all empty blocks
fn ifs_same_cond() {
let a = 0;
@@ -7,18 +12,18 @@ fn ifs_same_cond() {
if b {
} else if b {
- //~ ERROR ifs same condition
+ //~^ ERROR: this `if` has the same condition as a previous `if`
}
if a == 1 {
} else if a == 1 {
- //~ ERROR ifs same condition
+ //~^ ERROR: this `if` has the same condition as a previous `if`
}
if 2 * a == 1 {
} else if 2 * a == 2 {
} else if 2 * a == 1 {
- //~ ERROR ifs same condition
+ //~^ ERROR: this `if` has the same condition as a previous `if`
} else if a == 1 {
}
@@ -47,6 +52,7 @@ fn issue10272() {
let a = String::from("ha");
if a.contains("ah") {
} else if a.contains("ah") {
+ //~^ ERROR: this `if` has the same condition as a previous `if`
// Trigger this lint
} else if a.contains("ha") {
} else if a == "wow" {
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
index 9519f6904..8d7093447 100644
--- a/src/tools/clippy/tests/ui/ifs_same_cond.stderr
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
@@ -1,48 +1,48 @@
error: this `if` has the same condition as a previous `if`
- --> $DIR/ifs_same_cond.rs:9:15
+ --> $DIR/ifs_same_cond.rs:14:15
|
LL | } else if b {
| ^
|
note: same as this
- --> $DIR/ifs_same_cond.rs:8:8
+ --> $DIR/ifs_same_cond.rs:13:8
|
LL | if b {
| ^
= note: `-D clippy::ifs-same-cond` implied by `-D warnings`
error: this `if` has the same condition as a previous `if`
- --> $DIR/ifs_same_cond.rs:14:15
+ --> $DIR/ifs_same_cond.rs:19:15
|
LL | } else if a == 1 {
| ^^^^^^
|
note: same as this
- --> $DIR/ifs_same_cond.rs:13:8
+ --> $DIR/ifs_same_cond.rs:18:8
|
LL | if a == 1 {
| ^^^^^^
error: this `if` has the same condition as a previous `if`
- --> $DIR/ifs_same_cond.rs:20:15
+ --> $DIR/ifs_same_cond.rs:25:15
|
LL | } else if 2 * a == 1 {
| ^^^^^^^^^^
|
note: same as this
- --> $DIR/ifs_same_cond.rs:18:8
+ --> $DIR/ifs_same_cond.rs:23:8
|
LL | if 2 * a == 1 {
| ^^^^^^^^^^
error: this `if` has the same condition as a previous `if`
- --> $DIR/ifs_same_cond.rs:49:15
+ --> $DIR/ifs_same_cond.rs:54:15
|
LL | } else if a.contains("ah") {
| ^^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/ifs_same_cond.rs:48:8
+ --> $DIR/ifs_same_cond.rs:53:8
|
LL | if a.contains("ah") {
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs
index ca7c12213..7ed7bf94a 100644
--- a/src/tools/clippy/tests/ui/implicit_hasher.rs
+++ b/src/tools/clippy/tests/ui/implicit_hasher.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![deny(clippy::implicit_hasher)]
#![allow(unused)]
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
index 620d45e68..d84346e87 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
index 10ffadcb2..87fba7448 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed
new file mode 100644
index 000000000..ac482dcda
--- /dev/null
+++ b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed
@@ -0,0 +1,97 @@
+//@run-rustfix
+#![allow(clippy::clone_on_copy, unused)]
+#![no_main]
+
+// lint
+
+struct A(u32);
+
+impl Clone for A {
+ fn clone(&self) -> Self { *self }
+
+
+}
+
+impl Copy for A {}
+
+// do not lint
+
+struct B(u32);
+
+impl Clone for B {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl Copy for B {}
+
+// do not lint derived (clone's implementation is `*self` here anyway)
+
+#[derive(Clone, Copy)]
+struct C(u32);
+
+// do not lint derived (fr this time)
+
+struct D(u32);
+
+#[automatically_derived]
+impl Clone for D {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl Copy for D {}
+
+// do not lint if clone is not manually implemented
+
+struct E(u32);
+
+#[automatically_derived]
+impl Clone for E {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl Copy for E {}
+
+// lint since clone is not derived
+
+#[derive(Copy)]
+struct F(u32);
+
+impl Clone for F {
+ fn clone(&self) -> Self { *self }
+
+
+}
+
+// do not lint since copy has more restrictive bounds
+
+#[derive(Eq, PartialEq)]
+struct Uwu<A: Copy>(A);
+
+impl<A: Copy> Clone for Uwu<A> {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs
new file mode 100644
index 000000000..00775874f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs
@@ -0,0 +1,107 @@
+//@run-rustfix
+#![allow(clippy::clone_on_copy, unused)]
+#![no_main]
+
+// lint
+
+struct A(u32);
+
+impl Clone for A {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl Copy for A {}
+
+// do not lint
+
+struct B(u32);
+
+impl Clone for B {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl Copy for B {}
+
+// do not lint derived (clone's implementation is `*self` here anyway)
+
+#[derive(Clone, Copy)]
+struct C(u32);
+
+// do not lint derived (fr this time)
+
+struct D(u32);
+
+#[automatically_derived]
+impl Clone for D {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl Copy for D {}
+
+// do not lint if clone is not manually implemented
+
+struct E(u32);
+
+#[automatically_derived]
+impl Clone for E {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl Copy for E {}
+
+// lint since clone is not derived
+
+#[derive(Copy)]
+struct F(u32);
+
+impl Clone for F {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+// do not lint since copy has more restrictive bounds
+
+#[derive(Eq, PartialEq)]
+struct Uwu<A: Copy>(A);
+
+impl<A: Copy> Clone for Uwu<A> {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ source.clone();
+ *self = source.clone();
+ }
+}
+
+impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr
new file mode 100644
index 000000000..0021841aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr
@@ -0,0 +1,40 @@
+error: incorrect implementation of `clone` on a `Copy` type
+ --> $DIR/incorrect_clone_impl_on_copy_type.rs:10:29
+ |
+LL | fn clone(&self) -> Self {
+ | _____________________________^
+LL | | Self(self.0)
+LL | | }
+ | |_____^ help: change this to: `{ *self }`
+ |
+ = note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default
+
+error: incorrect implementation of `clone_from` on a `Copy` type
+ --> $DIR/incorrect_clone_impl_on_copy_type.rs:14:5
+ |
+LL | / fn clone_from(&mut self, source: &Self) {
+LL | | source.clone();
+LL | | *self = source.clone();
+LL | | }
+ | |_____^ help: remove this
+
+error: incorrect implementation of `clone` on a `Copy` type
+ --> $DIR/incorrect_clone_impl_on_copy_type.rs:81:29
+ |
+LL | fn clone(&self) -> Self {
+ | _____________________________^
+LL | | Self(self.0)
+LL | | }
+ | |_____^ help: change this to: `{ *self }`
+
+error: incorrect implementation of `clone_from` on a `Copy` type
+ --> $DIR/incorrect_clone_impl_on_copy_type.rs:85:5
+ |
+LL | / fn clone_from(&mut self, source: &Self) {
+LL | | source.clone();
+LL | | *self = source.clone();
+LL | | }
+ | |_____^ help: remove this
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
index 26abc9edb..16f9e47e8 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
@@ -3,7 +3,12 @@
// We also check the out_of_bounds_indexing lint here, because it lints similar things and
// we want to avoid false positives.
#![warn(clippy::out_of_bounds_indexing)]
-#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(
+ unconditional_panic,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::useless_vec
+)]
const ARR: [i32; 2] = [1, 2];
const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index 8fd77913a..f4357c1d5 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -1,5 +1,5 @@
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:9:20
+ --> $DIR/indexing_slicing_index.rs:14:20
|
LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re
= note: `-D clippy::indexing-slicing` implied by `-D warnings`
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:10:24
+ --> $DIR/indexing_slicing_index.rs:15:24
|
LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
| ^^^^^^^^^^^
@@ -18,19 +18,19 @@ LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
= note: the suggestion might not be applicable in constant blocks
error[E0080]: evaluation of `main::{constant#3}` failed
- --> $DIR/indexing_slicing_index.rs:31:14
+ --> $DIR/indexing_slicing_index.rs:36:14
|
LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
note: erroneous constant used
- --> $DIR/indexing_slicing_index.rs:31:5
+ --> $DIR/indexing_slicing_index.rs:36:5
|
LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^^^^^^^^^^^^^
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:22:5
+ --> $DIR/indexing_slicing_index.rs:27:5
|
LL | x[index];
| ^^^^^^^^
@@ -38,7 +38,7 @@ LL | x[index];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:30:14
+ --> $DIR/indexing_slicing_index.rs:35:14
|
LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^
@@ -47,7 +47,7 @@ LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restri
= note: the suggestion might not be applicable in constant blocks
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:31:14
+ --> $DIR/indexing_slicing_index.rs:36:14
|
LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restr
= note: the suggestion might not be applicable in constant blocks
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:38:5
+ --> $DIR/indexing_slicing_index.rs:43:5
|
LL | v[0];
| ^^^^
@@ -64,7 +64,7 @@ LL | v[0];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:39:5
+ --> $DIR/indexing_slicing_index.rs:44:5
|
LL | v[10];
| ^^^^^
@@ -72,7 +72,7 @@ LL | v[10];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:40:5
+ --> $DIR/indexing_slicing_index.rs:45:5
|
LL | v[1 << 3];
| ^^^^^^^^^
@@ -80,7 +80,7 @@ LL | v[1 << 3];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:46:5
+ --> $DIR/indexing_slicing_index.rs:51:5
|
LL | v[N];
| ^^^^
@@ -88,7 +88,7 @@ LL | v[N];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:47:5
+ --> $DIR/indexing_slicing_index.rs:52:5
|
LL | v[M];
| ^^^^
@@ -96,7 +96,7 @@ LL | v[M];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error[E0080]: evaluation of constant value failed
- --> $DIR/indexing_slicing_index.rs:10:24
+ --> $DIR/indexing_slicing_index.rs:15:24
|
LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs
index 7b107db39..939b6ac36 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs
+++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs
@@ -2,7 +2,7 @@
// We also check the out_of_bounds_indexing lint here, because it lints similar things and
// we want to avoid false positives.
#![warn(clippy::out_of_bounds_indexing)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec)]
fn main() {
let x = [1, 2, 3, 4];
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
index 9f550acb1..af197e33f 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
@@ -11,35 +11,35 @@ fn main() {
for _ in &vec![X, X] {}
let _ = vec![1, 2, 3].into_iter();
- let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter()
- let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter()
- let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
- let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
+ let _ = (&vec![1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::rc::Rc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::sync::Arc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter()
- let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
- let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
- let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut()
+ let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
- let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&Vec::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&VecDeque::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&LinkedList::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
- let _ = (&HashMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Some(4)).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Some(5)).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&Ok::<_, i32>(6)).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&Vec::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&VecDeque::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&LinkedList::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&HashMap::<i32, u64>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ ERROR: equivalent to `.iter_mut()
- let _ = (&BTreeSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&BinaryHeap::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
- let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter()
- let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter()
+ let _ = (&BTreeSet::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&BinaryHeap::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&HashSet::<i32>::new()).iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::path::Path::new("12/34").iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR: equivalent to `.iter()
- let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter()
+ let _ = (&[1, 2, 3]).iter().next(); //~ ERROR: equivalent to `.iter()
}
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
index 3381ae04d..3ac13d7dd 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.rs
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
@@ -11,35 +11,35 @@ fn main() {
for _ in &vec![X, X] {}
let _ = vec![1, 2, 3].into_iter();
- let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
- let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
- let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
- let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&vec![1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter()
- let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
- let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
- let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
+ let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter_mut()
- let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
- let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Some(4)).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Some(5)).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&Ok::<_, i32>(6)).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&Vec::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut Vec::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&VecDeque::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&LinkedList::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter_mut()
+ let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ ERROR: equivalent to `.iter_mut()
- let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
- let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
- let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
+ let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = (&HashSet::<i32>::new()).into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::path::Path::new("12/34").into_iter(); //~ ERROR: equivalent to `.iter()
+ let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR: equivalent to `.iter()
- let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
+ let _ = (&[1, 2, 3]).into_iter().next(); //~ ERROR: equivalent to `.iter()
}
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
index 28003b365..06014a93f 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
@@ -1,7 +1,7 @@
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:14:30
|
-LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&vec![1, 2, 3]).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
|
= note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
@@ -9,157 +9,157 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:15:46
|
-LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:16:41
|
-LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = std::rc::Rc::from(&[X][..]).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:17:44
|
-LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = std::sync::Arc::from(&[X][..]).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:19:32
|
-LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+LL | let _ = (&&&&&&&[1, 2, 3]).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:20:36
|
-LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:21:40
|
-LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
+LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option`
--> $DIR/into_iter_on_ref.rs:23:24
|
-LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&Some(4)).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option`
--> $DIR/into_iter_on_ref.rs:24:28
|
-LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut Some(5)).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result`
--> $DIR/into_iter_on_ref.rs:25:32
|
-LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&Ok::<_, i32>(6)).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result`
--> $DIR/into_iter_on_ref.rs:26:37
|
-LL | let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut Err::<i32, _>(7)).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:27:34
|
-LL | let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&Vec::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:28:38
|
-LL | let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut Vec::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap`
--> $DIR/into_iter_on_ref.rs:29:44
|
-LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap`
--> $DIR/into_iter_on_ref.rs:30:48
|
-LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque`
--> $DIR/into_iter_on_ref.rs:31:39
|
-LL | let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&VecDeque::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque`
--> $DIR/into_iter_on_ref.rs:32:43
|
-LL | let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut VecDeque::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList`
--> $DIR/into_iter_on_ref.rs:33:41
|
-LL | let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&LinkedList::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList`
--> $DIR/into_iter_on_ref.rs:34:45
|
-LL | let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut LinkedList::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap`
--> $DIR/into_iter_on_ref.rs:35:43
|
-LL | let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&HashMap::<i32, u64>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap`
--> $DIR/into_iter_on_ref.rs:36:47
|
-LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet`
--> $DIR/into_iter_on_ref.rs:38:39
|
-LL | let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&BTreeSet::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap`
--> $DIR/into_iter_on_ref.rs:39:41
|
-LL | let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&BinaryHeap::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet`
--> $DIR/into_iter_on_ref.rs:40:38
|
-LL | let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = (&HashSet::<i32>::new()).into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path`
--> $DIR/into_iter_on_ref.rs:41:43
|
-LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
+LL | let _ = std::path::Path::new("12/34").into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf`
--> $DIR/into_iter_on_ref.rs:42:47
|
-LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
+LL | let _ = std::path::PathBuf::from("12/34").into_iter();
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:44:26
|
-LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
+LL | let _ = (&[1, 2, 3]).into_iter().next();
| ^^^^^^^^^ help: call directly: `iter`
error: aborting due to 27 previous errors
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
deleted file mode 100644
index 3dc096d31..000000000
--- a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-#![warn(clippy::invalid_utf8_in_unchecked)]
-
-fn main() {
- // Valid
- unsafe {
- std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]);
- std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']);
- std::str::from_utf8_unchecked(b"clippy");
-
- let x = 0xA0;
- std::str::from_utf8_unchecked(&[0xC0, x]);
- }
-
- // Invalid
- unsafe {
- std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
- std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']);
- std::str::from_utf8_unchecked(b"cl\x82ippy");
- }
-}
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
deleted file mode 100644
index c89cd2758..000000000
--- a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error: non UTF-8 literal in `std::str::from_utf8_unchecked`
- --> $DIR/invalid_utf8_in_unchecked.rs:16:9
- |
-LL | std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings`
-
-error: non UTF-8 literal in `std::str::from_utf8_unchecked`
- --> $DIR/invalid_utf8_in_unchecked.rs:17:9
- |
-LL | std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: non UTF-8 literal in `std::str::from_utf8_unchecked`
- --> $DIR/invalid_utf8_in_unchecked.rs:18:9
- |
-LL | std::str::from_utf8_unchecked(b"cl/x82ippy");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs
index 586d13647..0b07de114 100644
--- a/src/tools/clippy/tests/ui/issue-3145.rs
+++ b/src/tools/clippy/tests/ui/issue-3145.rs
@@ -1,3 +1,3 @@
fn main() {
- println!("{}" a); //~ERROR expected `,`, found `a`
+ println!("{}" a); //~ERROR: expected `,`, found `a`
}
diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr
index a35032aa1..d7c2c88a2 100644
--- a/src/tools/clippy/tests/ui/issue-3145.stderr
+++ b/src/tools/clippy/tests/ui/issue-3145.stderr
@@ -1,7 +1,7 @@
error: expected `,`, found `a`
--> $DIR/issue-3145.rs:2:19
|
-LL | println!("{}" a); //~ERROR expected `,`, found `a`
+LL | println!("{}" a);
| ^ expected `,`
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr
index fd553aa45..5b60646ef 100644
--- a/src/tools/clippy/tests/ui/issue_4266.stderr
+++ b/src/tools/clippy/tests/ui/issue_4266.stderr
@@ -1,16 +1,16 @@
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/issue_4266.rs:4:1
+ --> $DIR/issue_4266.rs:4:16
|
LL | async fn sink1<'a>(_: &'a str) {} // lint
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/issue_4266.rs:8:1
+ --> $DIR/issue_4266.rs:8:21
|
LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
error: methods called `new` usually take no `self`
--> $DIR/issue_4266.rs:28:22
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
index 597f1b951..1b6257471 100644
--- 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
@@ -1,17 +1,2 @@
-error: items were found after the testing module
- --> $DIR/block_module.rs:13:1
- |
-LL | / mod tests {
-LL | | #[test]
-LL | | fn hi() {}
-LL | | }
-... |
-LL | | () => {};
-LL | | }
- | |_^
- |
- = help: move the items to before the testing module was defined
- = note: `-D clippy::items-after-test-module` implied by `-D warnings`
-
-error: aborting due to previous error
+error: Option 'test' given more than once
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
index 88f08bb99..2baea06f8 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
@@ -1,6 +1,7 @@
//@run-rustfix
#![allow(unused)]
+#![allow(clippy::useless_vec)]
use std::collections::HashSet;
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
index d3438b7f5..9eac94eb8 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.rs
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
@@ -1,6 +1,7 @@
//@run-rustfix
#![allow(unused)]
+#![allow(clippy::useless_vec)]
use std::collections::HashSet;
use std::collections::VecDeque;
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
index b2cc497bf..b38cf547d 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
@@ -1,5 +1,5 @@
error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
- --> $DIR/iter_cloned_collect.rs:10:27
+ --> $DIR/iter_cloned_collect.rs:11:27
|
LL | let v2: Vec<isize> = v.iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
@@ -7,13 +7,13 @@ LL | let v2: Vec<isize> = v.iter().cloned().collect();
= note: `-D clippy::iter-cloned-collect` implied by `-D warnings`
error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
- --> $DIR/iter_cloned_collect.rs:15:38
+ --> $DIR/iter_cloned_collect.rs:16:38
|
LL | let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
- --> $DIR/iter_cloned_collect.rs:20:24
+ --> $DIR/iter_cloned_collect.rs:21:24
|
LL | .to_bytes()
| ________________________^
@@ -23,13 +23,13 @@ LL | | .collect();
| |______________________^ help: try: `.to_vec()`
error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
- --> $DIR/iter_cloned_collect.rs:28:24
+ --> $DIR/iter_cloned_collect.rs:29:24
|
LL | let _: Vec<_> = arr.iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
- --> $DIR/iter_cloned_collect.rs:31:26
+ --> $DIR/iter_cloned_collect.rs:32:26
|
LL | let _: Vec<isize> = v.iter().copied().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
diff --git a/src/tools/clippy/tests/ui/iter_count.fixed b/src/tools/clippy/tests/ui/iter_count.fixed
index 4367a12f8..b62082014 100644
--- a/src/tools/clippy/tests/ui/iter_count.fixed
+++ b/src/tools/clippy/tests/ui/iter_count.fixed
@@ -7,7 +7,8 @@
array_into_iter,
unused_mut,
clippy::into_iter_on_ref,
- clippy::unnecessary_operation
+ clippy::unnecessary_operation,
+ clippy::useless_vec
)]
extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_count.rs b/src/tools/clippy/tests/ui/iter_count.rs
index 8c7543cf0..fb2161312 100644
--- a/src/tools/clippy/tests/ui/iter_count.rs
+++ b/src/tools/clippy/tests/ui/iter_count.rs
@@ -7,7 +7,8 @@
array_into_iter,
unused_mut,
clippy::into_iter_on_ref,
- clippy::unnecessary_operation
+ clippy::unnecessary_operation,
+ clippy::useless_vec
)]
extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_count.stderr b/src/tools/clippy/tests/ui/iter_count.stderr
index 2e3d7fc35..f9aee0b78 100644
--- a/src/tools/clippy/tests/ui/iter_count.stderr
+++ b/src/tools/clippy/tests/ui/iter_count.stderr
@@ -1,5 +1,5 @@
error: called `.iter().count()` on a `slice`
- --> $DIR/iter_count.rs:54:6
+ --> $DIR/iter_count.rs:55:6
|
LL | &vec[..].iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
@@ -7,145 +7,145 @@ LL | &vec[..].iter().count();
= note: `-D clippy::iter-count` implied by `-D warnings`
error: called `.iter().count()` on a `Vec`
- --> $DIR/iter_count.rs:55:5
+ --> $DIR/iter_count.rs:56:5
|
LL | vec.iter().count();
| ^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
error: called `.iter().count()` on a `slice`
- --> $DIR/iter_count.rs:56:5
+ --> $DIR/iter_count.rs:57:5
|
LL | boxed_slice.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()`
error: called `.iter().count()` on a `VecDeque`
- --> $DIR/iter_count.rs:57:5
+ --> $DIR/iter_count.rs:58:5
|
LL | vec_deque.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
error: called `.iter().count()` on a `HashSet`
- --> $DIR/iter_count.rs:58:5
+ --> $DIR/iter_count.rs:59:5
|
LL | hash_set.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()`
error: called `.iter().count()` on a `HashMap`
- --> $DIR/iter_count.rs:59:5
+ --> $DIR/iter_count.rs:60:5
|
LL | hash_map.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
error: called `.iter().count()` on a `BTreeMap`
- --> $DIR/iter_count.rs:60:5
+ --> $DIR/iter_count.rs:61:5
|
LL | b_tree_map.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
error: called `.iter().count()` on a `BTreeSet`
- --> $DIR/iter_count.rs:61:5
+ --> $DIR/iter_count.rs:62:5
|
LL | b_tree_set.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()`
error: called `.iter().count()` on a `LinkedList`
- --> $DIR/iter_count.rs:62:5
+ --> $DIR/iter_count.rs:63:5
|
LL | linked_list.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
error: called `.iter().count()` on a `BinaryHeap`
- --> $DIR/iter_count.rs:63:5
+ --> $DIR/iter_count.rs:64:5
|
LL | binary_heap.iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()`
error: called `.iter_mut().count()` on a `Vec`
- --> $DIR/iter_count.rs:65:5
+ --> $DIR/iter_count.rs:66:5
|
LL | vec.iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
error: called `.iter_mut().count()` on a `slice`
- --> $DIR/iter_count.rs:66:6
+ --> $DIR/iter_count.rs:67:6
|
LL | &vec[..].iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
error: called `.iter_mut().count()` on a `VecDeque`
- --> $DIR/iter_count.rs:67:5
+ --> $DIR/iter_count.rs:68:5
|
LL | vec_deque.iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
error: called `.iter_mut().count()` on a `HashMap`
- --> $DIR/iter_count.rs:68:5
+ --> $DIR/iter_count.rs:69:5
|
LL | hash_map.iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
error: called `.iter_mut().count()` on a `BTreeMap`
- --> $DIR/iter_count.rs:69:5
+ --> $DIR/iter_count.rs:70:5
|
LL | b_tree_map.iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
error: called `.iter_mut().count()` on a `LinkedList`
- --> $DIR/iter_count.rs:70:5
+ --> $DIR/iter_count.rs:71:5
|
LL | linked_list.iter_mut().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
error: called `.into_iter().count()` on a `slice`
- --> $DIR/iter_count.rs:72:6
+ --> $DIR/iter_count.rs:73:6
|
LL | &vec[..].into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
error: called `.into_iter().count()` on a `Vec`
- --> $DIR/iter_count.rs:73:5
+ --> $DIR/iter_count.rs:74:5
|
LL | vec.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
error: called `.into_iter().count()` on a `VecDeque`
- --> $DIR/iter_count.rs:74:5
+ --> $DIR/iter_count.rs:75:5
|
LL | vec_deque.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
error: called `.into_iter().count()` on a `HashSet`
- --> $DIR/iter_count.rs:75:5
+ --> $DIR/iter_count.rs:76:5
|
LL | hash_set.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()`
error: called `.into_iter().count()` on a `HashMap`
- --> $DIR/iter_count.rs:76:5
+ --> $DIR/iter_count.rs:77:5
|
LL | hash_map.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
error: called `.into_iter().count()` on a `BTreeMap`
- --> $DIR/iter_count.rs:77:5
+ --> $DIR/iter_count.rs:78:5
|
LL | b_tree_map.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
error: called `.into_iter().count()` on a `BTreeSet`
- --> $DIR/iter_count.rs:78:5
+ --> $DIR/iter_count.rs:79:5
|
LL | b_tree_set.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()`
error: called `.into_iter().count()` on a `LinkedList`
- --> $DIR/iter_count.rs:79:5
+ --> $DIR/iter_count.rs:80:5
|
LL | linked_list.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
error: called `.into_iter().count()` on a `BinaryHeap`
- --> $DIR/iter_count.rs:80:5
+ --> $DIR/iter_count.rs:81:5
|
LL | binary_heap.into_iter().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()`
diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs
new file mode 100644
index 000000000..548b799de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_next_loop.rs
@@ -0,0 +1,16 @@
+#![allow(dead_code, unused, for_loops_over_fallibles)]
+#![warn(clippy::iter_next_loop)]
+
+fn main() {
+ let x = [1, 2, 3, 4];
+ for _ in vec.iter().next() {}
+
+ struct Unrelated(&'static [u8]);
+ impl Unrelated {
+ fn next(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+ }
+ let u = Unrelated(&[0]);
+ for _v in u.next() {} // no error
+}
diff --git a/src/tools/clippy/tests/ui/iter_next_loop.stderr b/src/tools/clippy/tests/ui/iter_next_loop.stderr
new file mode 100644
index 000000000..5bba0e635
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_next_loop.stderr
@@ -0,0 +1,9 @@
+error[E0423]: expected value, found macro `vec`
+ --> $DIR/iter_next_loop.rs:6:14
+ |
+LL | for _ in vec.iter().next() {}
+ | ^^^ not a value
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.fixed b/src/tools/clippy/tests/ui/iter_next_slice.fixed
index d862abc34..702edccdb 100644
--- a/src/tools/clippy/tests/ui/iter_next_slice.fixed
+++ b/src/tools/clippy/tests/ui/iter_next_slice.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::iter_next_slice)]
+#![allow(clippy::useless_vec)]
fn main() {
// test code goes here
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.rs b/src/tools/clippy/tests/ui/iter_next_slice.rs
index da6fc46e4..30bfc72de 100644
--- a/src/tools/clippy/tests/ui/iter_next_slice.rs
+++ b/src/tools/clippy/tests/ui/iter_next_slice.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::iter_next_slice)]
+#![allow(clippy::useless_vec)]
fn main() {
// test code goes here
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.stderr b/src/tools/clippy/tests/ui/iter_next_slice.stderr
index d8b89061f..0db8201a1 100644
--- a/src/tools/clippy/tests/ui/iter_next_slice.stderr
+++ b/src/tools/clippy/tests/ui/iter_next_slice.stderr
@@ -1,5 +1,5 @@
error: using `.iter().next()` on an array
- --> $DIR/iter_next_slice.rs:9:13
+ --> $DIR/iter_next_slice.rs:10:13
|
LL | let _ = s.iter().next();
| ^^^^^^^^^^^^^^^ help: try calling: `s.first()`
@@ -7,19 +7,19 @@ LL | let _ = s.iter().next();
= note: `-D clippy::iter-next-slice` implied by `-D warnings`
error: using `.iter().next()` on a Slice without end index
- --> $DIR/iter_next_slice.rs:12:13
+ --> $DIR/iter_next_slice.rs:13:13
|
LL | let _ = s[2..].iter().next();
| ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)`
error: using `.iter().next()` on a Slice without end index
- --> $DIR/iter_next_slice.rs:15:13
+ --> $DIR/iter_next_slice.rs:16:13
|
LL | let _ = v[5..].iter().next();
| ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)`
error: using `.iter().next()` on an array
- --> $DIR/iter_next_slice.rs:18:13
+ --> $DIR/iter_next_slice.rs:19:13
|
LL | let _ = v.iter().next();
| ^^^^^^^^^^^^^^^ help: try calling: `v.first()`
diff --git a/src/tools/clippy/tests/ui/iter_nth.rs b/src/tools/clippy/tests/ui/iter_nth.rs
index e7fb97d4f..7c567bb81 100644
--- a/src/tools/clippy/tests/ui/iter_nth.rs
+++ b/src/tools/clippy/tests/ui/iter_nth.rs
@@ -1,6 +1,7 @@
//@aux-build:option_helpers.rs
#![warn(clippy::iter_nth)]
+#![allow(clippy::useless_vec)]
#[macro_use]
extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr
index a0fe353bc..24be81454 100644
--- a/src/tools/clippy/tests/ui/iter_nth.stderr
+++ b/src/tools/clippy/tests/ui/iter_nth.stderr
@@ -1,5 +1,5 @@
-error: called `.iter().nth()` on a Vec
- --> $DIR/iter_nth.rs:33:23
+error: called `.iter().nth()` on a `Vec`
+ --> $DIR/iter_nth.rs:34:23
|
LL | let bad_vec = some_vec.iter().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let bad_vec = some_vec.iter().nth(3);
= note: `-D clippy::iter-nth` implied by `-D warnings`
error: called `.iter().nth()` on a slice
- --> $DIR/iter_nth.rs:34:26
+ --> $DIR/iter_nth.rs:35:26
|
LL | let bad_slice = &some_vec[..].iter().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,23 +16,23 @@ LL | let bad_slice = &some_vec[..].iter().nth(3);
= help: calling `.get()` is both faster and more readable
error: called `.iter().nth()` on a slice
- --> $DIR/iter_nth.rs:35:31
+ --> $DIR/iter_nth.rs:36:31
|
LL | let bad_boxed_slice = boxed_slice.iter().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: calling `.get()` is both faster and more readable
-error: called `.iter().nth()` on a VecDeque
- --> $DIR/iter_nth.rs:36:29
+error: called `.iter().nth()` on a `VecDeque`
+ --> $DIR/iter_nth.rs:37:29
|
LL | let bad_vec_deque = some_vec_deque.iter().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: calling `.get()` is both faster and more readable
-error: called `.iter_mut().nth()` on a Vec
- --> $DIR/iter_nth.rs:41:23
+error: called `.iter_mut().nth()` on a `Vec`
+ --> $DIR/iter_nth.rs:42:23
|
LL | let bad_vec = some_vec.iter_mut().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,15 +40,15 @@ LL | let bad_vec = some_vec.iter_mut().nth(3);
= help: calling `.get_mut()` is both faster and more readable
error: called `.iter_mut().nth()` on a slice
- --> $DIR/iter_nth.rs:44:26
+ --> $DIR/iter_nth.rs:45:26
|
LL | let bad_slice = &some_vec[..].iter_mut().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: calling `.get_mut()` is both faster and more readable
-error: called `.iter_mut().nth()` on a VecDeque
- --> $DIR/iter_nth.rs:47:29
+error: called `.iter_mut().nth()` on a `VecDeque`
+ --> $DIR/iter_nth.rs:48:29
|
LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.fixed b/src/tools/clippy/tests/ui/iter_nth_zero.fixed
index 587b0d1d3..91f4a7ba0 100644
--- a/src/tools/clippy/tests/ui/iter_nth_zero.fixed
+++ b/src/tools/clippy/tests/ui/iter_nth_zero.fixed
@@ -29,3 +29,18 @@ fn main() {
let mut iter2 = s3.iter();
let _unwrapped = iter2.next().unwrap();
}
+
+struct Issue9820;
+
+impl Iterator for Issue9820 {
+ type Item = ();
+
+ fn nth(&mut self, _n: usize) -> Option<Self::Item> {
+ todo!()
+ }
+
+ // Don't lint in implementations of `next`, as calling `next` in `next` is incorrect
+ fn next(&mut self) -> Option<Self::Item> {
+ self.nth(0)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.rs b/src/tools/clippy/tests/ui/iter_nth_zero.rs
index 93b576ec5..160a895bb 100644
--- a/src/tools/clippy/tests/ui/iter_nth_zero.rs
+++ b/src/tools/clippy/tests/ui/iter_nth_zero.rs
@@ -29,3 +29,18 @@ fn main() {
let mut iter2 = s3.iter();
let _unwrapped = iter2.nth(0).unwrap();
}
+
+struct Issue9820;
+
+impl Iterator for Issue9820 {
+ type Item = ();
+
+ fn nth(&mut self, _n: usize) -> Option<Self::Item> {
+ todo!()
+ }
+
+ // Don't lint in implementations of `next`, as calling `next` in `next` is incorrect
+ fn next(&mut self) -> Option<Self::Item> {
+ self.nth(0)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
index bf576e9cb..2874513c0 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code, clippy::let_unit_value)]
+#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
index df42d88ef..26f39734a 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code, clippy::let_unit_value)]
+#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed
index 8f2cefc43..b888d965e 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.fixed
+++ b/src/tools/clippy/tests/ui/iter_skip_next.fixed
@@ -4,6 +4,7 @@
#![warn(clippy::iter_skip_next)]
#![allow(clippy::disallowed_names)]
#![allow(clippy::iter_nth)]
+#![allow(clippy::useless_vec)]
#![allow(unused_mut, dead_code)]
extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs
index 71d83384f..e44efdebc 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.rs
+++ b/src/tools/clippy/tests/ui/iter_skip_next.rs
@@ -4,6 +4,7 @@
#![warn(clippy::iter_skip_next)]
#![allow(clippy::disallowed_names)]
#![allow(clippy::iter_nth)]
+#![allow(clippy::useless_vec)]
#![allow(unused_mut, dead_code)]
extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr
index ca6970b27..4ee26e088 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.stderr
+++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr
@@ -1,5 +1,5 @@
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:16:28
+ --> $DIR/iter_skip_next.rs:17:28
|
LL | let _ = some_vec.iter().skip(42).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
@@ -7,37 +7,37 @@ LL | let _ = some_vec.iter().skip(42).next();
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:17:36
+ --> $DIR/iter_skip_next.rs:18:36
|
LL | let _ = some_vec.iter().cycle().skip(42).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:18:20
+ --> $DIR/iter_skip_next.rs:19:20
|
LL | let _ = (1..10).skip(10).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:19:33
+ --> $DIR/iter_skip_next.rs:20:33
|
LL | let _ = &some_vec[..].iter().skip(3).next();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:27:26
+ --> $DIR/iter_skip_next.rs:28:26
|
LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:29:29
+ --> $DIR/iter_skip_next.rs:30:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
error: called `skip(..).next()` on an iterator
- --> $DIR/iter_skip_next.rs:35:29
+ --> $DIR/iter_skip_next.rs:36:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
diff --git a/src/tools/clippy/tests/ui/iter_with_drain.fixed b/src/tools/clippy/tests/ui/iter_with_drain.fixed
index 24a95c4d0..7a8c67701 100644
--- a/src/tools/clippy/tests/ui/iter_with_drain.fixed
+++ b/src/tools/clippy/tests/ui/iter_with_drain.fixed
@@ -2,7 +2,7 @@
// will emits unused mut warnings after fixing
#![allow(unused_mut)]
// will emits needless collect warnings after fixing
-#![allow(clippy::needless_collect)]
+#![allow(clippy::needless_collect, clippy::drain_collect)]
#![warn(clippy::iter_with_drain)]
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
diff --git a/src/tools/clippy/tests/ui/iter_with_drain.rs b/src/tools/clippy/tests/ui/iter_with_drain.rs
index a118c981e..cf3a935c3 100644
--- a/src/tools/clippy/tests/ui/iter_with_drain.rs
+++ b/src/tools/clippy/tests/ui/iter_with_drain.rs
@@ -2,7 +2,7 @@
// will emits unused mut warnings after fixing
#![allow(unused_mut)]
// will emits needless collect warnings after fixing
-#![allow(clippy::needless_collect)]
+#![allow(clippy::needless_collect, clippy::drain_collect)]
#![warn(clippy::iter_with_drain)]
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.rs b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs
index 13d1cfd42..33ec78e9a 100644
--- a/src/tools/clippy/tests/ui/iterator_step_by_zero.rs
+++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::useless_vec)]
#[warn(clippy::iterator_step_by_zero)]
fn main() {
let _ = vec!["A", "B", "B"].iter().step_by(0);
diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
index d792aea11..b470e2ed2 100644
--- a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
+++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
@@ -1,5 +1,5 @@
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:3:13
+ --> $DIR/iterator_step_by_zero.rs:4:13
|
LL | let _ = vec!["A", "B", "B"].iter().step_by(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,37 +7,37 @@ LL | let _ = vec!["A", "B", "B"].iter().step_by(0);
= note: `-D clippy::iterator-step-by-zero` implied by `-D warnings`
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:4:13
+ --> $DIR/iterator_step_by_zero.rs:5:13
|
LL | let _ = "XXX".chars().step_by(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:5:13
+ --> $DIR/iterator_step_by_zero.rs:6:13
|
LL | let _ = (0..1).step_by(0);
| ^^^^^^^^^^^^^^^^^
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:14:13
+ --> $DIR/iterator_step_by_zero.rs:15:13
|
LL | let _ = (1..).step_by(0);
| ^^^^^^^^^^^^^^^^
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:15:13
+ --> $DIR/iterator_step_by_zero.rs:16:13
|
LL | let _ = (1..=2).step_by(0);
| ^^^^^^^^^^^^^^^^^^
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:18:13
+ --> $DIR/iterator_step_by_zero.rs:19:13
|
LL | let _ = x.step_by(0);
| ^^^^^^^^^^^^
error: `Iterator::step_by(0)` will panic at runtime
- --> $DIR/iterator_step_by_zero.rs:22:13
+ --> $DIR/iterator_step_by_zero.rs:23:13
|
LL | let _ = v1.iter().step_by(2 / 3);
| ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs
index ea8bc5b4a..e677cc9a7 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.rs
+++ b/src/tools/clippy/tests/ui/large_enum_variant.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(dead_code)]
#![allow(unused_variables)]
diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs
index 4a8ba995d..e0f6b3d9d 100644
--- a/src/tools/clippy/tests/ui/large_futures.rs
+++ b/src/tools/clippy/tests/ui/large_futures.rs
@@ -1,5 +1,6 @@
#![feature(generators)]
#![warn(clippy::large_futures)]
+#![allow(clippy::never_loop)]
#![allow(clippy::future_not_send)]
#![allow(clippy::manual_async_fn)]
diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr
index 67e0fceff..5bcf05488 100644
--- a/src/tools/clippy/tests/ui/large_futures.stderr
+++ b/src/tools/clippy/tests/ui/large_futures.stderr
@@ -1,5 +1,5 @@
error: large future with a size of 16385 bytes
- --> $DIR/large_futures.rs:10:9
+ --> $DIR/large_futures.rs:11:9
|
LL | big_fut([0u8; 1024 * 16]).await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))`
@@ -7,37 +7,37 @@ LL | big_fut([0u8; 1024 * 16]).await;
= note: `-D clippy::large-futures` implied by `-D warnings`
error: large future with a size of 16386 bytes
- --> $DIR/large_futures.rs:12:5
+ --> $DIR/large_futures.rs:13:5
|
LL | f.await
| ^ help: consider `Box::pin` on it: `Box::pin(f)`
error: large future with a size of 16387 bytes
- --> $DIR/large_futures.rs:16:9
+ --> $DIR/large_futures.rs:17:9
|
LL | wait().await;
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
error: large future with a size of 16387 bytes
- --> $DIR/large_futures.rs:20:13
+ --> $DIR/large_futures.rs:21:13
|
LL | wait().await;
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
error: large future with a size of 65540 bytes
- --> $DIR/large_futures.rs:27:5
+ --> $DIR/large_futures.rs:28:5
|
LL | foo().await;
| ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())`
error: large future with a size of 49159 bytes
- --> $DIR/large_futures.rs:28:5
+ --> $DIR/large_futures.rs:29:5
|
LL | calls_fut(fut).await;
| ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))`
error: large future with a size of 65540 bytes
- --> $DIR/large_futures.rs:40:5
+ --> $DIR/large_futures.rs:41:5
|
LL | / async {
LL | | let x = [0i32; 1024 * 16];
@@ -56,7 +56,7 @@ LL + })
|
error: large future with a size of 65540 bytes
- --> $DIR/large_futures.rs:51:13
+ --> $DIR/large_futures.rs:52:13
|
LL | / async {
LL | | let x = [0i32; 1024 * 16];
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs
index 99787ffd3..3e9d5e6a4 100644
--- a/src/tools/clippy/tests/ui/large_stack_arrays.rs
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs
@@ -18,6 +18,19 @@ pub static DOESNOTLINT2: [u8; 512_001] = {
[x; 512_001]
};
+fn issue_10741() {
+ #[derive(Copy, Clone)]
+ struct Large([u32; 100_000]);
+
+ fn build() -> Large {
+ Large([0; 100_000])
+ }
+
+ let _x = [build(); 3];
+
+ let _y = [build(), build(), build()];
+}
+
fn main() {
let bad = (
[0u32; 20_000_000],
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
index 24e900949..118d39566 100644
--- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
@@ -1,14 +1,30 @@
error: allocating a local array larger than 512000 bytes
- --> $DIR/large_stack_arrays.rs:23:9
+ --> $DIR/large_stack_arrays.rs:29:14
+ |
+LL | let _x = [build(); 3];
+ | ^^^^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![build(); 3].into_boxed_slice()`
+ = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
+
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:31:14
+ |
+LL | let _y = [build(), build(), build()];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
+
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:36:9
|
LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
- = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
error: allocating a local array larger than 512000 bytes
- --> $DIR/large_stack_arrays.rs:24:9
+ --> $DIR/large_stack_arrays.rs:37:9
|
LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +32,7 @@ LL | [S { data: [0; 32] }; 5000],
= help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
- --> $DIR/large_stack_arrays.rs:25:9
+ --> $DIR/large_stack_arrays.rs:38:9
|
LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +40,7 @@ LL | [Some(""); 20_000_000],
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
- --> $DIR/large_stack_arrays.rs:26:9
+ --> $DIR/large_stack_arrays.rs:39:9
|
LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^
@@ -32,12 +48,12 @@ LL | [E::T(0); 5000],
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
- --> $DIR/large_stack_arrays.rs:27:9
+ --> $DIR/large_stack_arrays.rs:40:9
|
LL | [0u8; usize::MAX],
| ^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
diff --git a/src/tools/clippy/tests/ui/large_stack_frames.rs b/src/tools/clippy/tests/ui/large_stack_frames.rs
new file mode 100644
index 000000000..cd9d0c8a6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_stack_frames.rs
@@ -0,0 +1,44 @@
+#![allow(unused, incomplete_features)]
+#![warn(clippy::large_stack_frames)]
+#![feature(unsized_locals)]
+
+use std::hint::black_box;
+
+fn generic<T: Default>() {
+ let x = T::default();
+ black_box(&x);
+}
+
+fn unsized_local() {
+ let x: dyn std::fmt::Display = *(Box::new(1) as Box<dyn std::fmt::Display>);
+ black_box(&x);
+}
+
+struct ArrayDefault<const N: usize>([u8; N]);
+
+impl<const N: usize> Default for ArrayDefault<N> {
+ fn default() -> Self {
+ Self([0; N])
+ }
+}
+
+fn many_small_arrays() {
+ let x = [0u8; 500_000];
+ let x2 = [0u8; 500_000];
+ let x3 = [0u8; 500_000];
+ let x4 = [0u8; 500_000];
+ let x5 = [0u8; 500_000];
+ black_box((&x, &x2, &x3, &x4, &x5));
+}
+
+fn large_return_value() -> ArrayDefault<1_000_000> {
+ Default::default()
+}
+
+fn large_fn_arg(x: ArrayDefault<1_000_000>) {
+ black_box(&x);
+}
+
+fn main() {
+ generic::<ArrayDefault<1_000_000>>();
+}
diff --git a/src/tools/clippy/tests/ui/large_stack_frames.stderr b/src/tools/clippy/tests/ui/large_stack_frames.stderr
new file mode 100644
index 000000000..d57df8596
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_stack_frames.stderr
@@ -0,0 +1,37 @@
+error: this function allocates a large amount of stack space
+ --> $DIR/large_stack_frames.rs:25:1
+ |
+LL | / fn many_small_arrays() {
+LL | | let x = [0u8; 500_000];
+LL | | let x2 = [0u8; 500_000];
+LL | | let x3 = [0u8; 500_000];
+... |
+LL | | black_box((&x, &x2, &x3, &x4, &x5));
+LL | | }
+ | |_^
+ |
+ = note: allocating large amounts of stack space can overflow the stack
+ = note: `-D clippy::large-stack-frames` implied by `-D warnings`
+
+error: this function allocates a large amount of stack space
+ --> $DIR/large_stack_frames.rs:34:1
+ |
+LL | / fn large_return_value() -> ArrayDefault<1_000_000> {
+LL | | Default::default()
+LL | | }
+ | |_^
+ |
+ = note: allocating large amounts of stack space can overflow the stack
+
+error: this function allocates a large amount of stack space
+ --> $DIR/large_stack_frames.rs:38:1
+ |
+LL | / fn large_fn_arg(x: ArrayDefault<1_000_000>) {
+LL | | black_box(&x);
+LL | | }
+ | |_^
+ |
+ = note: allocating large amounts of stack space can overflow the stack
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed
index 2c22abd7e..fafee6a0d 100644
--- a/src/tools/clippy/tests/ui/len_zero.fixed
+++ b/src/tools/clippy/tests/ui/len_zero.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::len_zero)]
-#![allow(dead_code, unused, clippy::len_without_is_empty)]
+#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)]
extern crate core;
use core::ops::Deref;
diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs
index a011ff976..6a9006c47 100644
--- a/src/tools/clippy/tests/ui/len_zero.rs
+++ b/src/tools/clippy/tests/ui/len_zero.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::len_zero)]
-#![allow(dead_code, unused, clippy::len_without_is_empty)]
+#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)]
extern crate core;
use core::ops::Deref;
diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.rs b/src/tools/clippy/tests/ui/let_underscore_untyped.rs
index 2c313ff35..431d83778 100644
--- a/src/tools/clippy/tests/ui/let_underscore_untyped.rs
+++ b/src/tools/clippy/tests/ui/let_underscore_untyped.rs
@@ -1,4 +1,4 @@
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::let_underscore_untyped)]
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
index ae1a480bc..8214176cf 100644
--- a/src/tools/clippy/tests/ui/let_with_type_underscore.rs
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
@@ -1,4 +1,4 @@
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::let_with_type_underscore)]
#![allow(clippy::let_unit_value, clippy::needless_late_init)]
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.fixed b/src/tools/clippy/tests/ui/lossy_float_literal.fixed
index a20885756..e19f4980c 100644
--- a/src/tools/clippy/tests/ui/lossy_float_literal.fixed
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::lossy_float_literal)]
+#![allow(overflowing_literals, unused)]
fn main() {
// Lossy whole-number float literals
@@ -32,4 +33,7 @@ fn main() {
let _: f64 = 1e99;
let _: f64 = 1E99;
let _: f32 = 0.1;
+
+ const INF1: f32 = 1000000000000000000000000000000000f32;
+ const NEG_INF1: f32 = -340282357000000000000000000000000000001_f32;
}
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.rs b/src/tools/clippy/tests/ui/lossy_float_literal.rs
index 1a75f214c..a2a1cfb31 100644
--- a/src/tools/clippy/tests/ui/lossy_float_literal.rs
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::lossy_float_literal)]
+#![allow(overflowing_literals, unused)]
fn main() {
// Lossy whole-number float literals
@@ -32,4 +33,7 @@ fn main() {
let _: f64 = 1e99;
let _: f64 = 1E99;
let _: f32 = 0.1;
+
+ const INF1: f32 = 1000000000000000000000000000000000f32;
+ const NEG_INF1: f32 = -340282357000000000000000000000000000001_f32;
}
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
index d2193c0c8..2d72b1643 100644
--- a/src/tools/clippy/tests/ui/lossy_float_literal.stderr
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
@@ -1,5 +1,5 @@
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:6:18
+ --> $DIR/lossy_float_literal.rs:7:18
|
LL | let _: f32 = 16_777_217.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
@@ -7,61 +7,61 @@ LL | let _: f32 = 16_777_217.0;
= note: `-D clippy::lossy-float-literal` implied by `-D warnings`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:7:18
+ --> $DIR/lossy_float_literal.rs:8:18
|
LL | let _: f32 = 16_777_219.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:8:18
+ --> $DIR/lossy_float_literal.rs:9:18
|
LL | let _: f32 = 16_777_219.;
| ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:9:18
+ --> $DIR/lossy_float_literal.rs:10:18
|
LL | let _: f32 = 16_777_219.000;
| ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:10:13
+ --> $DIR/lossy_float_literal.rs:11:13
|
LL | let _ = 16_777_219f32;
| ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:11:19
+ --> $DIR/lossy_float_literal.rs:12:19
|
LL | let _: f32 = -16_777_219.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:12:18
+ --> $DIR/lossy_float_literal.rs:13:18
|
LL | let _: f64 = 9_007_199_254_740_993.0;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:13:18
+ --> $DIR/lossy_float_literal.rs:14:18
|
LL | let _: f64 = 9_007_199_254_740_993.;
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:14:18
+ --> $DIR/lossy_float_literal.rs:15:18
|
LL | let _: f64 = 9_007_199_254_740_993.00;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:15:13
+ --> $DIR/lossy_float_literal.rs:16:13
|
LL | let _ = 9_007_199_254_740_993f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64`
error: literal cannot be represented as the underlying type without loss of precision
- --> $DIR/lossy_float_literal.rs:16:19
+ --> $DIR/lossy_float_literal.rs:17:19
|
LL | let _: f64 = -9_007_199_254_740_993.0;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index b4dabe3ca..53b6a0250 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -1,6 +1,6 @@
//@aux-build:macro_rules.rs
//@aux-build:macro_use_helper.rs
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
//@run-rustfix
//@ignore-32bit
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
index 925a2c61f..a40fa3898 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -1,6 +1,6 @@
//@aux-build:macro_rules.rs
//@aux-build:macro_use_helper.rs
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
//@run-rustfix
//@ignore-32bit
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
index 6fd338cef..67a833e85 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.stderr
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -1,28 +1,28 @@
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:25:5
+ --> $DIR/macro_use_imports.rs:19:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
|
= note: `-D clippy::macro-use-imports` implied by `-D warnings`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:21:5
+ --> $DIR/macro_use_imports.rs:23:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:23:5
+ --> $DIR/macro_use_imports.rs:25:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:19:5
+ --> $DIR/macro_use_imports.rs:21:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
index b9677851b..3971aadbe 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
@@ -1,6 +1,6 @@
//@aux-build:macro_rules.rs
//@aux-build:macro_use_helper.rs
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
//@ignore-32bit
#![feature(lint_reasons)]
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
index ab9b375dc..d8dde0236 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
@@ -5,7 +5,7 @@
#![warn(clippy::manual_assert)]
#![allow(dead_code, unused_doc_comments)]
-#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
+#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)]
macro_rules! one {
() => {
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
index ab9b375dc..d8dde0236 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
@@ -5,7 +5,7 @@
#![warn(clippy::manual_assert)]
#![allow(dead_code, unused_doc_comments)]
-#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
+#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)]
macro_rules! one {
() => {
diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs
index eac52d1b5..0f87d6e2d 100644
--- a/src/tools/clippy/tests/ui/manual_assert.rs
+++ b/src/tools/clippy/tests/ui/manual_assert.rs
@@ -5,7 +5,7 @@
#![warn(clippy::manual_assert)]
#![allow(dead_code, unused_doc_comments)]
-#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
+#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)]
macro_rules! one {
() => {
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed
index e458f0d25..e609b4b1b 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.fixed
+++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::manual_async_fn)]
-#![allow(unused)]
+#![allow(clippy::needless_pub_self, unused)]
use std::future::Future;
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs
index dd5ca1c9b..6c1a9edaa 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.rs
+++ b/src/tools/clippy/tests/ui/manual_async_fn.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::manual_async_fn)]
-#![allow(unused)]
+#![allow(clippy::needless_pub_self, unused)]
use std::future::Future;
diff --git a/src/tools/clippy/tests/ui/manual_filter.fixed b/src/tools/clippy/tests/ui/manual_filter.fixed
index 755caa664..5e3b12e51 100644
--- a/src/tools/clippy/tests/ui/manual_filter.fixed
+++ b/src/tools/clippy/tests/ui/manual_filter.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::manual_filter)]
-#![allow(unused_variables, dead_code)]
+#![allow(unused_variables, dead_code, clippy::useless_vec)]
fn main() {
Some(0).filter(|&x| x <= 0);
diff --git a/src/tools/clippy/tests/ui/manual_filter.rs b/src/tools/clippy/tests/ui/manual_filter.rs
index faccfe9db..b81604b03 100644
--- a/src/tools/clippy/tests/ui/manual_filter.rs
+++ b/src/tools/clippy/tests/ui/manual_filter.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::manual_filter)]
-#![allow(unused_variables, dead_code)]
+#![allow(unused_variables, dead_code, clippy::useless_vec)]
fn main() {
match Some(0) {
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.fixed b/src/tools/clippy/tests/ui/manual_filter_map.fixed
index 831323089..9dd376df2 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 @@
#![allow(dead_code)]
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+#![allow(clippy::useless_vec)]
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 2692303d3..6dd1e066a 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 @@
#![allow(dead_code)]
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+#![allow(clippy::useless_vec)]
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 6e5bbe8f2..882468b0f 100644
--- a/src/tools/clippy/tests/ui/manual_filter_map.stderr
+++ b/src/tools/clippy/tests/ui/manual_filter_map.stderr
@@ -1,5 +1,5 @@
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))`
@@ -7,19 +7,19 @@ LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap
= note: `-D clippy::manual-filter-map` implied by `-D warnings`
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))`
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())`
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())
| __________^
@@ -27,7 +27,7 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
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())
| __________^
@@ -35,7 +35,7 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
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())
| __________^
@@ -43,7 +43,7 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).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())
| __________^
@@ -51,7 +51,7 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).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())`
@@ -59,67 +59,67 @@ LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()
= note: `-D clippy::manual-find-map` implied by `-D warnings`
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())`
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())`
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())
| __________^
@@ -127,7 +127,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())
| __________^
@@ -135,7 +135,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())
| __________^
@@ -143,7 +143,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())
| __________^
@@ -151,7 +151,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())
| __________^
@@ -159,7 +159,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())
| __________^
@@ -167,7 +167,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())
| __________^
@@ -175,7 +175,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())
| __________^
@@ -183,7 +183,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())
| __________^
diff --git a/src/tools/clippy/tests/ui/manual_find_map.fixed b/src/tools/clippy/tests/ui/manual_find_map.fixed
index 554613a30..0c8eebf04 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 @@
#![allow(dead_code)]
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+#![allow(clippy::useless_vec)]
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 d6245758f..b2feb48a8 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 @@
#![allow(dead_code)]
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+#![allow(clippy::useless_vec)]
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 c1ac499f7..693a06bb5 100644
--- a/src/tools/clippy/tests/ui/manual_find_map.stderr
+++ b/src/tools/clippy/tests/ui/manual_find_map.stderr
@@ -1,5 +1,5 @@
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))`
@@ -7,19 +7,19 @@ LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()
= note: `-D clippy::manual-find-map` implied by `-D warnings`
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))`
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())`
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())
| __________^
@@ -27,7 +27,7 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
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())
| __________^
@@ -35,7 +35,7 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
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())
| __________^
@@ -43,7 +43,7 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).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())
| __________^
@@ -51,91 +51,91 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).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())`
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())`
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())
| __________^
@@ -143,7 +143,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())
| __________^
@@ -151,7 +151,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())
| __________^
@@ -159,7 +159,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())
| __________^
@@ -167,7 +167,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())
| __________^
@@ -175,7 +175,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())
| __________^
@@ -183,7 +183,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())
| __________^
@@ -191,7 +191,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())
| __________^
@@ -199,7 +199,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_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs
index 3996d775f..46241afec 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else.rs
@@ -4,7 +4,8 @@
clippy::unused_unit,
clippy::let_unit_value,
clippy::match_single_binding,
- clippy::never_loop
+ clippy::never_loop,
+ clippy::needless_if
)]
#![warn(clippy::manual_let_else)]
@@ -127,8 +128,8 @@ fn fire() {
return;
};
- // Tuples supported for the identity block and pattern
- let v = if let (Some(v_some), w_some) = (g(), 0) {
+ // Tuples supported with multiple bindings
+ let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
(w_some, v_some)
} else {
return;
@@ -146,10 +147,44 @@ fn fire() {
Variant::A(0, 0)
}
- // Should not be renamed
let v = if let Variant::A(a, 0) = e() { a } else { return };
- // Should be renamed
- let v = if let Variant::B(b) = e() { b } else { return };
+
+ // `mut v` is inserted into the pattern
+ let mut v = if let Variant::B(b) = e() { b } else { return };
+
+ // Nesting works
+ let nested = Ok(Some(e()));
+ let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
+ b
+ } else {
+ return;
+ };
+ // dot dot works
+ let v = if let Variant::A(.., a) = e() { a } else { return };
+
+ // () is preserved: a bit of an edge case but make sure it stays around
+ let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
+
+ // Tuple structs work
+ let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
+ x
+ } else {
+ return;
+ };
+
+ // Field init shorthand is suggested
+ let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
+ x
+ } else {
+ return;
+ };
+
+ // Multi-field structs also work
+ let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
+ (x, v, w)
+ } else {
+ return;
+ };
}
fn not_fire() {
@@ -274,4 +309,23 @@ fn not_fire() {
};
1
};
+
+ // This would require creation of a suggestion of the form
+ // let v @ (Some(_), _) = (...) else { return };
+ // Which is too advanced for our code, so we just bail.
+ let v = if let (Some(v_some), w_some) = (g(), 0) {
+ (w_some, v_some)
+ } else {
+ return;
+ };
+}
+
+struct S<T> {
+ v: T,
+}
+
+struct U<T> {
+ v: T,
+ w: T,
+ x: T,
}
diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr
index f6f56f7b0..1eada4f99 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else.stderr
@@ -1,5 +1,5 @@
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:24:5
+ --> $DIR/manual_let_else.rs:25:5
|
LL | let v = if let Some(v_some) = g() { v_some } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
@@ -7,7 +7,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return };
= note: `-D clippy::manual-let-else` implied by `-D warnings`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:25:5
+ --> $DIR/manual_let_else.rs:26:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -24,7 +24,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:31:5
+ --> $DIR/manual_let_else.rs:32:5
|
LL | / let v = if let Some(v) = g() {
LL | | // Blocks around the identity should have no impact
@@ -45,25 +45,25 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:44:9
+ --> $DIR/manual_let_else.rs:45: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:45:9
+ --> $DIR/manual_let_else.rs:46: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:49:5
+ --> $DIR/manual_let_else.rs:50: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:52:5
+ --> $DIR/manual_let_else.rs:53:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -80,7 +80,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:59:5
+ --> $DIR/manual_let_else.rs:60:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -97,7 +97,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:66:5
+ --> $DIR/manual_let_else.rs:67:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -116,7 +116,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:76:5
+ --> $DIR/manual_let_else.rs:77:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -138,13 +138,13 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:86:5
+ --> $DIR/manual_let_else.rs:87: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:89:5
+ --> $DIR/manual_let_else.rs:90:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -165,7 +165,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:98:5
+ --> $DIR/manual_let_else.rs:99:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -186,7 +186,7 @@ LL + } };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:107:5
+ --> $DIR/manual_let_else.rs:108:5
|
LL | / let v = if let Some(v_some) = g() {
LL | | v_some
@@ -215,7 +215,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:124:5
+ --> $DIR/manual_let_else.rs:125:5
|
LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
LL | | v_some
@@ -232,9 +232,9 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:131:5
+ --> $DIR/manual_let_else.rs:132:5
|
-LL | / let v = if let (Some(v_some), w_some) = (g(), 0) {
+LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
LL | | (w_some, v_some)
LL | | } else {
LL | | return;
@@ -243,13 +243,13 @@ LL | | };
|
help: consider writing
|
-LL ~ let (Some(v_some), w_some) = (g(), 0) else {
+LL ~ let (Some(S { v }), w) = (g().map(|_| S { v: 0 }), 0) else {
LL + return;
LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:140:13
+ --> $DIR/manual_let_else.rs:141:13
|
LL | let $n = if let Some(v) = $e { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };`
@@ -263,16 +263,96 @@ error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:150:5
|
LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() 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:153: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:157:5
+ |
+LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
+LL | | b
+LL | | } else {
+LL | | return;
+LL | | };
+ | |______^
+ |
+help: consider writing
+ |
+LL ~ let (Ok(Some(Variant::B(v))) | Err(Some(Variant::A(v, _)))) = nested else {
+LL + return;
+LL + };
+ |
+
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else.rs:163: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:166: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:169:5
+ |
+LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
+LL | | x
+LL | | } else {
+LL | | return;
+LL | | };
+ | |______^
+ |
+help: consider writing
+ |
+LL ~ let Some(S { v: w }) = Some(S { v: 0 }) else {
+LL + return;
+LL + };
+ |
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:152:5
+ --> $DIR/manual_let_else.rs:176:5
+ |
+LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
+LL | | x
+LL | | } else {
+LL | | return;
+LL | | };
+ | |______^
+ |
+help: consider writing
+ |
+LL ~ let Some(S { v }) = Some(S { v: 0 }) else {
+LL + return;
+LL + };
+ |
+
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else.rs:183:5
+ |
+LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
+LL | | (x, v, w)
+LL | | } else {
+LL | | return;
+LL | | };
+ | |______^
+ |
+help: consider writing
+ |
+LL ~ let Some(U { v: S { v }, w, x }) = None::<U<S<()>>> else {
+LL + return;
+LL + };
|
-LL | let v = if let Variant::B(b) = e() { b } else { return };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:262:5
+ --> $DIR/manual_let_else.rs:297:5
|
LL | / let _ = match ff {
LL | | Some(value) => value,
@@ -280,5 +360,5 @@ LL | | _ => macro_call!(),
LL | | };
| |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };`
-error: aborting due to 20 previous errors
+error: aborting due to 26 previous errors
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 73b746791..73ff69eec 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs
@@ -1,5 +1,9 @@
#![allow(unused_braces, unused_variables, dead_code)]
-#![allow(clippy::collapsible_else_if, clippy::let_unit_value)]
+#![allow(
+ clippy::collapsible_else_if,
+ clippy::let_unit_value,
+ clippy::redundant_at_rest_pattern
+)]
#![warn(clippy::manual_let_else)]
// Ensure that we don't conflict with match -> if let lints
#![warn(clippy::single_match_else, clippy::single_match)]
@@ -68,7 +72,12 @@ fn fire() {
let f = Variant::Bar(1);
let _value = match f {
- Variant::Bar(_) | Variant::Baz(_) => (),
+ Variant::Bar(v) | Variant::Baz(v) => v,
+ _ => return,
+ };
+
+ let _value = match Some(build_enum()) {
+ Some(Variant::Bar(v) | Variant::Baz(v)) => v,
_ => return,
};
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 bacc14dc9..3fd9a6376 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr
@@ -1,5 +1,5 @@
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:32:5
+ --> $DIR/manual_let_else_match.rs:36:5
|
LL | / let v = match g() {
LL | | Some(v_some) => v_some,
@@ -10,7 +10,7 @@ LL | | };
= note: `-D clippy::manual-let-else` implied by `-D warnings`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:37:5
+ --> $DIR/manual_let_else_match.rs:41:5
|
LL | / let v = match g() {
LL | | Some(v_some) => v_some,
@@ -19,7 +19,7 @@ LL | | };
| |______^ help: consider writing: `let Some(v) = g() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:44:9
+ --> $DIR/manual_let_else_match.rs:48:9
|
LL | / let v = match h() {
LL | | (Some(v), None) | (None, Some(v)) => v,
@@ -28,7 +28,7 @@ LL | | };
| |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:49:9
+ --> $DIR/manual_let_else_match.rs:53:9
|
LL | / let v = match build_enum() {
LL | | Variant::Bar(v) | Variant::Baz(v) => v,
@@ -37,7 +37,7 @@ LL | | };
| |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:57:5
+ --> $DIR/manual_let_else_match.rs:61:5
|
LL | / let v = match f() {
LL | | Ok(v) => v,
@@ -46,7 +46,7 @@ LL | | };
| |______^ help: consider writing: `let Ok(v) = f() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:63:5
+ --> $DIR/manual_let_else_match.rs:67:5
|
LL | / let v = match f().map_err(|_| ()) {
LL | | Ok(v) => v,
@@ -55,16 +55,25 @@ LL | | };
| |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:70:5
+ --> $DIR/manual_let_else_match.rs:74:5
|
LL | / let _value = match f {
-LL | | Variant::Bar(_) | Variant::Baz(_) => (),
+LL | | Variant::Bar(v) | Variant::Baz(v) => v,
LL | | _ => return,
LL | | };
- | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };`
+ | |______^ help: consider writing: `let (Variant::Bar(_value) | Variant::Baz(_value)) = f else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else_match.rs:76:5
+ --> $DIR/manual_let_else_match.rs:79:5
+ |
+LL | / let _value = match Some(build_enum()) {
+LL | | Some(Variant::Bar(v) | Variant::Baz(v)) => v,
+LL | | _ => return,
+LL | | };
+ | |______^ help: consider writing: `let Some(Variant::Bar(_value) | Variant::Baz(_value)) = Some(build_enum()) else { return };`
+
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else_match.rs:85:5
|
LL | / let data = match data.as_slice() {
LL | | [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0] => data,
@@ -72,5 +81,5 @@ 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 8 previous errors
+error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs
index ea0535d07..4d5c70f19 100644
--- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs
+++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs
@@ -1,4 +1,5 @@
#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+#![allow(clippy::useless_vec)]
const LOOP_OFFSET: usize = 5000;
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr
index c163ae061..1c6a7d5c0 100644
--- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr
+++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr
@@ -1,5 +1,5 @@
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:7:5
+ --> $DIR/without_loop_counters.rs:8:5
|
LL | / for i in 0..src.len() {
LL | | dst[i] = src[i];
@@ -9,7 +9,7 @@ LL | | }
= note: `-D clippy::manual-memcpy` implied by `-D warnings`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:12:5
+ --> $DIR/without_loop_counters.rs:13:5
|
LL | / for i in 0..src.len() {
LL | | dst[i + 10] = src[i];
@@ -17,7 +17,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:17:5
+ --> $DIR/without_loop_counters.rs:18:5
|
LL | / for i in 0..src.len() {
LL | | dst[i] = src[i + 10];
@@ -25,7 +25,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:22:5
+ --> $DIR/without_loop_counters.rs:23:5
|
LL | / for i in 11..src.len() {
LL | | dst[i] = src[i - 10];
@@ -33,7 +33,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:27:5
+ --> $DIR/without_loop_counters.rs:28:5
|
LL | / for i in 0..dst.len() {
LL | | dst[i] = src[i];
@@ -41,7 +41,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:40:5
+ --> $DIR/without_loop_counters.rs:41:5
|
LL | / for i in 10..256 {
LL | | dst[i] = src[i - 5];
@@ -56,7 +56,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]);
|
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:52:5
+ --> $DIR/without_loop_counters.rs:53:5
|
LL | / for i in 10..LOOP_OFFSET {
LL | | dst[i + LOOP_OFFSET] = src[i - some_var];
@@ -64,7 +64,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:65:5
+ --> $DIR/without_loop_counters.rs:66:5
|
LL | / for i in 0..src_vec.len() {
LL | | dst_vec[i] = src_vec[i];
@@ -72,7 +72,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:94:5
+ --> $DIR/without_loop_counters.rs:95:5
|
LL | / for i in from..from + src.len() {
LL | | dst[i] = src[i - from];
@@ -80,7 +80,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:98:5
+ --> $DIR/without_loop_counters.rs:99:5
|
LL | / for i in from..from + 3 {
LL | | dst[i] = src[i - from];
@@ -88,7 +88,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:103:5
+ --> $DIR/without_loop_counters.rs:104:5
|
LL | / for i in 0..5 {
LL | | dst[i - 0] = src[i];
@@ -96,7 +96,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:108:5
+ --> $DIR/without_loop_counters.rs:109:5
|
LL | / for i in 0..0 {
LL | | dst[i] = src[i];
@@ -104,7 +104,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);`
error: it looks like you're manually copying between slices
- --> $DIR/without_loop_counters.rs:131:5
+ --> $DIR/without_loop_counters.rs:132:5
|
LL | / for i in 0..src.len() {
LL | | dst[i] = src[i].clone();
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
new file mode 100644
index 000000000..9eee8f371
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
@@ -0,0 +1,35 @@
+//@run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::manual_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ let f = 6;
+
+ let _ = matches!(f, 1..=10);
+ let _ = matches!(f, 1..=10);
+ let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 8 | 10); // 7 is missing
+ let _ = matches!(f, | 4);
+ let _ = matches!(f, 4 | 5);
+ let _ = matches!(f, 1 | 2147483647);
+ let _ = matches!(f, 0 | 2147483647);
+ let _ = matches!(f, -2147483647 | 2147483647);
+ let _ = matches!(f, 1 | (2..=4));
+ let _ = matches!(f, 1 | (2..4));
+ let _ = matches!(f, 1..=48324729);
+ let _ = matches!(f, 0..=48324730);
+ let _ = matches!(f, 0..=3);
+ #[allow(clippy::match_like_matches_macro)]
+ let _ = match f {
+ 1..=10 => true,
+ _ => false,
+ };
+
+ macro_rules! mac {
+ ($e:expr) => {
+ matches!($e, 1..=10)
+ };
+ }
+ mac!(f);
+}
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs
new file mode 100644
index 000000000..10743a7d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs
@@ -0,0 +1,35 @@
+//@run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::manual_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ let f = 6;
+
+ let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10);
+ let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10);
+ let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 8 | 10); // 7 is missing
+ let _ = matches!(f, | 4);
+ let _ = matches!(f, 4 | 5);
+ let _ = matches!(f, 1 | 2147483647);
+ let _ = matches!(f, 0 | 2147483647);
+ let _ = matches!(f, -2147483647 | 2147483647);
+ let _ = matches!(f, 1 | (2..=4));
+ let _ = matches!(f, 1 | (2..4));
+ let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729);
+ let _ = matches!(f, 0 | (1..=10) | 48324730 | (2..=13) | (14..=48324728) | 48324729);
+ let _ = matches!(f, 0..=1 | 0..=2 | 0..=3);
+ #[allow(clippy::match_like_matches_macro)]
+ let _ = match f {
+ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true,
+ _ => false,
+ };
+
+ macro_rules! mac {
+ ($e:expr) => {
+ matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)
+ };
+ }
+ mac!(f);
+}
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.stderr b/src/tools/clippy/tests/ui/manual_range_patterns.stderr
new file mode 100644
index 000000000..bc9e33501
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.stderr
@@ -0,0 +1,51 @@
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:10:25
+ |
+LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
+ |
+ = note: `-D clippy::manual-range-patterns` implied by `-D warnings`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:11:25
+ |
+LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:20:25
+ |
+LL | let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=48324729`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:21:25
+ |
+LL | let _ = matches!(f, 0 | (1..=10) | 48324730 | (2..=13) | (14..=48324728) | 48324729);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=48324730`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:22:25
+ |
+LL | let _ = matches!(f, 0..=1 | 0..=2 | 0..=3);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=3`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:25:9
+ |
+LL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
+
+error: this OR pattern can be rewritten using a range
+ --> $DIR/manual_range_patterns.rs:31:26
+ |
+LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
+...
+LL | mac!(f);
+ | ------- in this macro invocation
+ |
+ = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index f2e44e56f..594a76897 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::manual_rem_euclid)]
#![allow(clippy::let_with_type_underscore)]
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index b2329c33a..d5f98e715 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::manual_rem_euclid)]
#![allow(clippy::let_with_type_underscore)]
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
index ac85bd8d3..5b9629f4b 100644
--- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::manual_slice_size_calculation)]
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
index 1f824b12b..297887a9c 100644
--- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused)]
#![warn(clippy::manual_slice_size_calculation)]
diff --git a/src/tools/clippy/tests/ui/manual_try_fold.rs b/src/tools/clippy/tests/ui/manual_try_fold.rs
new file mode 100644
index 000000000..4521e9fa1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_try_fold.rs
@@ -0,0 +1,100 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(clippy::unnecessary_fold, unused)]
+#![warn(clippy::manual_try_fold)]
+#![feature(try_trait_v2)]
+
+use std::ops::ControlFlow;
+use std::ops::FromResidual;
+use std::ops::Try;
+
+#[macro_use]
+extern crate proc_macros;
+
+// Test custom `Try` with more than 1 argument
+struct NotOption(i32, i32);
+
+impl<R> FromResidual<R> for NotOption {
+ fn from_residual(_: R) -> Self {
+ todo!()
+ }
+}
+
+impl Try for NotOption {
+ type Output = ();
+ type Residual = ();
+
+ fn from_output(_: Self::Output) -> Self {
+ todo!()
+ }
+
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ todo!()
+ }
+}
+
+// Test custom `Try` with only 1 argument
+#[derive(Default)]
+struct NotOptionButWorse(i32);
+
+impl<R> FromResidual<R> for NotOptionButWorse {
+ fn from_residual(_: R) -> Self {
+ todo!()
+ }
+}
+
+impl Try for NotOptionButWorse {
+ type Output = ();
+ type Residual = ();
+
+ fn from_output(_: Self::Output) -> Self {
+ todo!()
+ }
+
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ todo!()
+ }
+}
+
+fn main() {
+ [1, 2, 3]
+ .iter()
+ .fold(Some(0i32), |sum, i| sum?.checked_add(*i))
+ .unwrap();
+ [1, 2, 3]
+ .iter()
+ .fold(NotOption(0i32, 0i32), |sum, i| NotOption(0i32, 0i32));
+ [1, 2, 3]
+ .iter()
+ .fold(NotOptionButWorse(0i32), |sum, i| NotOptionButWorse(0i32));
+ // Do not lint
+ [1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap();
+ [1, 2, 3].iter().fold(0i32, |sum, i| sum + i);
+ [1, 2, 3]
+ .iter()
+ .fold(NotOptionButWorse::default(), |sum, i| NotOptionButWorse::default());
+ external! {
+ [1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i)).unwrap();
+ [1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap();
+ }
+ with_span! {
+ span
+ [1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i)).unwrap();
+ [1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap();
+ }
+}
+
+#[clippy::msrv = "1.26.0"]
+fn msrv_too_low() {
+ [1, 2, 3]
+ .iter()
+ .fold(Some(0i32), |sum, i| sum?.checked_add(*i))
+ .unwrap();
+}
+
+#[clippy::msrv = "1.27.0"]
+fn msrv_juust_right() {
+ [1, 2, 3]
+ .iter()
+ .fold(Some(0i32), |sum, i| sum?.checked_add(*i))
+ .unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/manual_try_fold.stderr b/src/tools/clippy/tests/ui/manual_try_fold.stderr
new file mode 100644
index 000000000..a0cf5b3b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_try_fold.stderr
@@ -0,0 +1,28 @@
+error: usage of `Iterator::fold` on a type that implements `Try`
+ --> $DIR/manual_try_fold.rs:61:10
+ |
+LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)`
+ |
+ = note: `-D clippy::manual-try-fold` implied by `-D warnings`
+
+error: usage of `Iterator::fold` on a type that implements `Try`
+ --> $DIR/manual_try_fold.rs:65:10
+ |
+LL | .fold(NotOption(0i32, 0i32), |sum, i| NotOption(0i32, 0i32));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(..., |sum, i| ...)`
+
+error: usage of `Iterator::fold` on a type that implements `Try`
+ --> $DIR/manual_try_fold.rs:68:10
+ |
+LL | .fold(NotOptionButWorse(0i32), |sum, i| NotOptionButWorse(0i32));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)`
+
+error: usage of `Iterator::fold` on a type that implements `Try`
+ --> $DIR/manual_try_fold.rs:98:10
+ |
+LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
index c17634bff..20560b87c 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![allow(dead_code)]
-#![allow(unused_variables, clippy::unnecessary_wraps)]
+#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)]
fn option_unwrap_or() {
// int case
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
index 6d49a6949..5dbc57565 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![allow(dead_code)]
-#![allow(unused_variables, clippy::unnecessary_wraps)]
+#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)]
fn option_unwrap_or() {
// int case
diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed
index d7474f357..50c0eb1a8 100644
--- a/src/tools/clippy/tests/ui/map_clone.fixed
+++ b/src/tools/clippy/tests/ui/map_clone.fixed
@@ -4,7 +4,8 @@
clippy::clone_on_copy,
clippy::iter_cloned_collect,
clippy::many_single_char_names,
- clippy::redundant_clone
+ clippy::redundant_clone,
+ clippy::useless_vec
)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs
index 74978ae80..91a084f28 100644
--- a/src/tools/clippy/tests/ui/map_clone.rs
+++ b/src/tools/clippy/tests/ui/map_clone.rs
@@ -4,7 +4,8 @@
clippy::clone_on_copy,
clippy::iter_cloned_collect,
clippy::many_single_char_names,
- clippy::redundant_clone
+ clippy::redundant_clone,
+ clippy::useless_vec
)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr
index d84a5bf8d..d768af1f4 100644
--- a/src/tools/clippy/tests/ui/map_clone.stderr
+++ b/src/tools/clippy/tests/ui/map_clone.stderr
@@ -1,5 +1,5 @@
error: you are using an explicit closure for copying elements
- --> $DIR/map_clone.rs:11:22
+ --> $DIR/map_clone.rs:12:22
|
LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
@@ -7,31 +7,31 @@ LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
= note: `-D clippy::map-clone` implied by `-D warnings`
error: you are using an explicit closure for cloning elements
- --> $DIR/map_clone.rs:12:26
+ --> $DIR/map_clone.rs:13:26
|
LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
error: you are using an explicit closure for copying elements
- --> $DIR/map_clone.rs:13:23
+ --> $DIR/map_clone.rs:14:23
|
LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
error: you are using an explicit closure for copying elements
- --> $DIR/map_clone.rs:15:26
+ --> $DIR/map_clone.rs:16:26
|
LL | let _: Option<u64> = Some(&16).map(|b| *b);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()`
error: you are using an explicit closure for copying elements
- --> $DIR/map_clone.rs:16:25
+ --> $DIR/map_clone.rs:17:25
|
LL | let _: Option<u8> = Some(&1).map(|x| x.clone());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()`
error: you are needlessly cloning iterator elements
- --> $DIR/map_clone.rs:27:29
+ --> $DIR/map_clone.rs:28:29
|
LL | let _ = std::env::args().map(|v| v.clone());
| ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs
index cb25d8567..bb36cb2c5 100644
--- a/src/tools/clippy/tests/ui/map_unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs
@@ -56,6 +56,10 @@ fn option_methods() {
.unwrap_or_else(||
0
);
+
+ // Check for `map(f).unwrap_or(false)` use.
+ let _ = opt.map(|x| x > 5).unwrap_or(false);
+
}
#[rustfmt::skip]
@@ -94,3 +98,46 @@ fn msrv_1_41() {
let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
}
+
+#[clippy::msrv = "1.69"]
+fn msrv_1_69() {
+ let opt: Option<i32> = Some(1);
+
+ let _ = opt.map(|x| x > 5).unwrap_or(false);
+}
+
+#[clippy::msrv = "1.70"]
+fn msrv_1_70() {
+ let opt: Option<i32> = Some(1);
+
+ let _ = opt.map(|x| x > 5).unwrap_or(false);
+}
+
+mod issue_10579 {
+ // Different variations of the same issue.
+ fn v1() {
+ let x = vec![1, 2, 3, 0];
+ let y = x.strip_suffix(&[0]).map(|s| s.to_vec()).unwrap_or(x);
+ println!("{y:?}");
+ }
+ fn v2() {
+ let x = vec![1, 2, 3, 0];
+ let y = Some(()).map(|_| x.to_vec()).unwrap_or(x);
+ println!("{y:?}");
+ }
+ fn v3() {
+ let x = vec![1, 2, 3, 0];
+ let xref = &x;
+ let y = Some(()).map(|_| xref.to_vec()).unwrap_or(x);
+ println!("{y:?}");
+ }
+ fn v4() {
+ struct VecInStruct {
+ v: Vec<u8>,
+ }
+ let s = VecInStruct { v: vec![1, 2, 3, 0] };
+
+ let y = Some(()).map(|_| s.v.clone()).unwrap_or(s.v);
+ println!("{y:?}");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
index 41781b050..9f4a4a9ae 100644
--- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
@@ -126,8 +126,20 @@ LL | | 0
LL | | );
| |_________^
+error: called `map(<f>).unwrap_or(false)` on an `Option` value. This can be done more directly by calling `is_some_and(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:61:13
+ |
+LL | let _ = opt.map(|x| x > 5).unwrap_or(false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `is_some_and(<f>)` instead
+ |
+LL - let _ = opt.map(|x| x > 5).unwrap_or(false);
+LL + let _ = opt.is_some_and(|x| x > 5);
+ |
+
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:67:13
+ --> $DIR/map_unwrap_or.rs:71:13
|
LL | let _ = res.map(|x| {
| _____________^
@@ -137,7 +149,7 @@ LL | | ).unwrap_or_else(|_e| 0);
| |____________________________^
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:71:13
+ --> $DIR/map_unwrap_or.rs:75:13
|
LL | let _ = res.map(|x| x + 1)
| _____________^
@@ -147,10 +159,34 @@ LL | | });
| |__________^
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:95:13
+ --> $DIR/map_unwrap_or.rs:99:13
|
LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
-error: aborting due to 12 previous errors
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:106:13
+ |
+LL | let _ = opt.map(|x| x > 5).unwrap_or(false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `map_or(<a>, <f>)` instead
+ |
+LL - let _ = opt.map(|x| x > 5).unwrap_or(false);
+LL + let _ = opt.map_or(false, |x| x > 5);
+ |
+
+error: called `map(<f>).unwrap_or(false)` on an `Option` value. This can be done more directly by calling `is_some_and(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:113:13
+ |
+LL | let _ = opt.map(|x| x > 5).unwrap_or(false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `is_some_and(<f>)` instead
+ |
+LL - let _ = opt.map(|x| x > 5).unwrap_or(false);
+LL + let _ = opt.is_some_and(|x| x > 5);
+ |
+
+error: aborting due to 15 previous errors
diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs
index 30415e3b9..cf9c279cd 100644
--- a/src/tools/clippy/tests/ui/match_on_vec_items.rs
+++ b/src/tools/clippy/tests/ui/match_on_vec_items.rs
@@ -1,4 +1,5 @@
#![warn(clippy::match_on_vec_items)]
+#![allow(clippy::redundant_at_rest_pattern, clippy::useless_vec)]
fn match_with_wildcard() {
let arr = vec![0, 1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.stderr b/src/tools/clippy/tests/ui/match_on_vec_items.stderr
index 49446d715..9b1f05286 100644
--- a/src/tools/clippy/tests/ui/match_on_vec_items.stderr
+++ b/src/tools/clippy/tests/ui/match_on_vec_items.stderr
@@ -1,5 +1,5 @@
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:9:11
+ --> $DIR/match_on_vec_items.rs:10:11
|
LL | match arr[idx] {
| ^^^^^^^^ help: try this: `arr.get(idx)`
@@ -7,43 +7,43 @@ LL | match arr[idx] {
= note: `-D clippy::match-on-vec-items` implied by `-D warnings`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:16:11
+ --> $DIR/match_on_vec_items.rs:17:11
|
LL | match arr[range] {
| ^^^^^^^^^^ help: try this: `arr.get(range)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:29:11
+ --> $DIR/match_on_vec_items.rs:30:11
|
LL | match arr[idx] {
| ^^^^^^^^ help: try this: `arr.get(idx)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:36:11
+ --> $DIR/match_on_vec_items.rs:37:11
|
LL | match arr[range] {
| ^^^^^^^^^^ help: try this: `arr.get(range)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:49:11
+ --> $DIR/match_on_vec_items.rs:50:11
|
LL | match arr[idx] {
| ^^^^^^^^ help: try this: `arr.get(idx)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:56:11
+ --> $DIR/match_on_vec_items.rs:57:11
|
LL | match arr[range] {
| ^^^^^^^^^^ help: try this: `arr.get(range)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:69:11
+ --> $DIR/match_on_vec_items.rs:70:11
|
LL | match arr[idx] {
| ^^^^^^^^ help: try this: `arr.get(idx)`
error: indexing into a vector may panic
- --> $DIR/match_on_vec_items.rs:76:11
+ --> $DIR/match_on_vec_items.rs:77:11
|
LL | match arr[range] {
| ^^^^^^^^^^ help: try this: `arr.get(range)`
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
index b4097fa96..b78c1fd06 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
@@ -1,7 +1,7 @@
#![feature(exclusive_range_pattern)]
#![warn(clippy::match_overlapping_arm)]
#![allow(clippy::redundant_pattern_matching)]
-#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)]
/// Tests for match_overlapping_arm
diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs
index 3914b4546..fad6a7db9 100644
--- a/src/tools/clippy/tests/ui/match_same_arms.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms.rs
@@ -8,29 +8,30 @@ pub enum Abc {
fn match_same_arms() {
let _ = match Abc::A {
- Abc::A => 0,
+ Abc::A => 0, //~ ERROR: this match arm has an identical body to the `_` wildcard arm
Abc::B => 1,
- _ => 0, //~ ERROR match arms have same body
+ _ => 0,
};
match (1, 2, 3) {
- (1, .., 3) => 42,
- (.., 3) => 42, //~ ERROR match arms have same body
+ (1, .., 3) => 42, //~ ERROR: this match arm has an identical body to another arm
+ (.., 3) => 42,
_ => 0,
};
let _ = match 42 {
42 => 1,
- 51 => 1, //~ ERROR match arms have same body
- 41 => 2,
- 52 => 2, //~ ERROR match arms have same body
+ 51 => 1, //~ ERROR: this match arm has an identical body to another arm
+ 41 => 2, //~ ERROR: this match arm has an identical body to another arm
+ 52 => 2,
_ => 0,
};
let _ = match 42 {
1 => 2,
- 2 => 2, //~ ERROR 2nd matched arms have same body
- 3 => 2, //~ ERROR 3rd matched arms have same body
+ 2 => 2, //~ ERROR: this match arm has an identical body to another arm
+ //~^ ERROR: this match arm has an identical body to another arm
+ 3 => 2, //~ ERROR: this match arm has an identical body to another arm
4 => 3,
_ => 0,
};
@@ -48,6 +49,7 @@ mod issue4244 {
match self {
CommandInfo::BuiltIn { name, .. } => name.to_string(),
CommandInfo::External { name, .. } => name.to_string(),
+ //~^ ERROR: this match arm has an identical body to another arm
}
}
}
diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr
index db85b5964..88b9a20a3 100644
--- a/src/tools/clippy/tests/ui/match_same_arms.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms.stderr
@@ -8,7 +8,7 @@ LL | Abc::A => 0,
note: `_` wildcard arm here
--> $DIR/match_same_arms.rs:13:9
|
-LL | _ => 0, //~ ERROR match arms have same body
+LL | _ => 0,
| ^^^^^^
= note: `-D clippy::match-same-arms` implied by `-D warnings`
@@ -24,13 +24,13 @@ LL | (1, .., 3) => 42,
note: other arm here
--> $DIR/match_same_arms.rs:18:9
|
-LL | (.., 3) => 42, //~ ERROR match arms have same body
+LL | (.., 3) => 42,
| ^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms.rs:24:9
|
-LL | 51 => 1, //~ ERROR match arms have same body
+LL | 51 => 1,
| --^^^^^
| |
| help: try merging the arm patterns: `51 | 42`
@@ -54,13 +54,13 @@ LL | 41 => 2,
note: other arm here
--> $DIR/match_same_arms.rs:26:9
|
-LL | 52 => 2, //~ ERROR match arms have same body
+LL | 52 => 2,
| ^^^^^^^
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms.rs:32:9
|
-LL | 2 => 2, //~ ERROR 2nd matched arms have same body
+LL | 2 => 2,
| -^^^^^
| |
| help: try merging the arm patterns: `2 | 1`
@@ -73,9 +73,9 @@ LL | 1 => 2,
| ^^^^^^
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms.rs:33:9
+ --> $DIR/match_same_arms.rs:34:9
|
-LL | 3 => 2, //~ ERROR 3rd matched arms have same body
+LL | 3 => 2,
| -^^^^^
| |
| help: try merging the arm patterns: `3 | 1`
@@ -90,20 +90,20 @@ LL | 1 => 2,
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms.rs:32:9
|
-LL | 2 => 2, //~ ERROR 2nd matched arms have same body
+LL | 2 => 2,
| -^^^^^
| |
| help: try merging the arm patterns: `2 | 3`
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms.rs:33:9
+ --> $DIR/match_same_arms.rs:34:9
|
-LL | 3 => 2, //~ ERROR 3rd matched arms have same body
+LL | 3 => 2,
| ^^^^^^
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms.rs:50:17
+ --> $DIR/match_same_arms.rs:51:17
|
LL | CommandInfo::External { name, .. } => name.to_string(),
| ----------------------------------^^^^^^^^^^^^^^^^^^^^
@@ -112,7 +112,7 @@ LL | CommandInfo::External { name, .. } => name.to_string(),
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms.rs:49:17
+ --> $DIR/match_same_arms.rs:50:17
|
LL | CommandInfo::BuiltIn { name, .. } => name.to_string(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs
index 60b2975be..b1b9a6ae3 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms2.rs
@@ -13,6 +13,7 @@ fn foo() -> bool {
fn match_same_arms() {
let _ = match 42 {
42 => {
+ //~^ ERROR: this match arm has an identical body to the `_` wildcard arm
foo();
let mut a = 42 + [23].len() as i32;
if true {
@@ -22,7 +23,6 @@ fn match_same_arms() {
a
},
_ => {
- //~ ERROR match arms have same body
foo();
let mut a = 42 + [23].len() as i32;
if true {
@@ -35,13 +35,13 @@ fn match_same_arms() {
let _ = match 42 {
42 => foo(),
- 51 => foo(), //~ ERROR match arms have same body
+ 51 => foo(), //~ ERROR: this match arm has an identical body to another arm
_ => true,
};
let _ = match Some(42) {
Some(_) => 24,
- None => 24, //~ ERROR match arms have same body
+ None => 24, //~ ERROR: this match arm has an identical body to another arm
};
let _ = match Some(42) {
@@ -63,13 +63,13 @@ fn match_same_arms() {
match (Some(42), Some(42)) {
(Some(a), None) => bar(a),
- (None, Some(a)) => bar(a), //~ ERROR match arms have same body
+ (None, Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm
_ => (),
}
match (Some(42), Some(42)) {
- (Some(a), ..) => bar(a),
- (.., Some(a)) => bar(a), //~ ERROR match arms have same body
+ (Some(a), ..) => bar(a), //~ ERROR: this match arm has an identical body to another arm
+ (.., Some(a)) => bar(a),
_ => (),
}
@@ -102,7 +102,7 @@ fn match_same_arms() {
}
match (x, Some(1i32)) {
- (Ok(x), Some(_)) => println!("ok {}", x),
+ (Ok(x), Some(_)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm
(Ok(_), Some(x)) => println!("ok {}", x),
_ => println!("err"),
}
@@ -118,7 +118,7 @@ fn match_same_arms() {
match x {
Ok(_tmp) => println!("ok"),
Ok(3) => println!("ok"),
- Ok(_) => println!("ok"),
+ Ok(_) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm
Err(_) => {
unreachable!();
},
@@ -146,6 +146,7 @@ fn match_same_arms() {
empty!(0);
},
1 => {
+ //~^ ERROR: this match arm has an identical body to another arm
empty!(0);
},
x => {
@@ -195,7 +196,7 @@ fn main() {
// Suggest moving `Foo::Z(_)` up.
let _ = match Foo::X(0) {
- Foo::X(0) => 1,
+ Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm
Foo::X(_) | Foo::Y(_) => 2,
Foo::Z(_) => 1,
_ => 0,
@@ -205,7 +206,7 @@ fn main() {
let _ = match Foo::X(0) {
Foo::X(0) => 1,
Foo::Y(_) | Foo::Z(0) => 2,
- Foo::Z(_) => 1,
+ Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm
_ => 0,
};
@@ -228,7 +229,7 @@ fn main() {
Some(Bar { x: 0, y: 5, .. }) => 1,
Some(Bar { y: 10, z: 0, .. }) => 2,
None => 50,
- Some(Bar { y: 0, x: 5, .. }) => 1,
+ Some(Bar { y: 0, x: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm
_ => 200,
};
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr
index 8fb461bd2..7f0c70745 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr
@@ -2,9 +2,9 @@ error: this match arm has an identical body to the `_` wildcard arm
--> $DIR/match_same_arms2.rs:15:9
|
LL | / 42 => {
+LL | |
LL | | foo();
LL | | let mut a = 42 + [23].len() as i32;
-LL | | if true {
... |
LL | | a
LL | | },
@@ -12,12 +12,12 @@ LL | | },
|
= help: or try changing either arm body
note: `_` wildcard arm here
- --> $DIR/match_same_arms2.rs:24:9
+ --> $DIR/match_same_arms2.rs:25:9
|
LL | / _ => {
-LL | | //~ ERROR match arms have same body
LL | | foo();
LL | | let mut a = 42 + [23].len() as i32;
+LL | | if true {
... |
LL | | a
LL | | },
@@ -27,7 +27,7 @@ LL | | },
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms2.rs:38:9
|
-LL | 51 => foo(), //~ ERROR match arms have same body
+LL | 51 => foo(),
| --^^^^^^^^^
| |
| help: try merging the arm patterns: `51 | 42`
@@ -42,7 +42,7 @@ LL | 42 => foo(),
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms2.rs:44:9
|
-LL | None => 24, //~ ERROR match arms have same body
+LL | None => 24,
| ----^^^^^^
| |
| help: try merging the arm patterns: `None | Some(_)`
@@ -57,7 +57,7 @@ LL | Some(_) => 24,
error: this match arm has an identical body to another arm
--> $DIR/match_same_arms2.rs:66:9
|
-LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body
+LL | (None, Some(a)) => bar(a),
| ---------------^^^^^^^^^^
| |
| help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
@@ -81,7 +81,7 @@ LL | (Some(a), ..) => bar(a),
note: other arm here
--> $DIR/match_same_arms2.rs:72:9
|
-LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body
+LL | (.., Some(a)) => bar(a),
| ^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
@@ -121,6 +121,7 @@ LL | 1 => {
| ^ help: try merging the arm patterns: `1 | 0`
| _________|
| |
+LL | |
LL | | empty!(0);
LL | | },
| |_________^
@@ -135,7 +136,7 @@ LL | | },
| |_________^
error: match expression looks like `matches!` macro
- --> $DIR/match_same_arms2.rs:166:16
+ --> $DIR/match_same_arms2.rs:167:16
|
LL | let _ans = match x {
| ________________^
@@ -148,7 +149,7 @@ LL | | };
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms2.rs:198:9
+ --> $DIR/match_same_arms2.rs:199:9
|
LL | Foo::X(0) => 1,
| ---------^^^^^
@@ -157,13 +158,13 @@ LL | Foo::X(0) => 1,
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms2.rs:200:9
+ --> $DIR/match_same_arms2.rs:201:9
|
LL | Foo::Z(_) => 1,
| ^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms2.rs:208:9
+ --> $DIR/match_same_arms2.rs:209:9
|
LL | Foo::Z(_) => 1,
| ---------^^^^^
@@ -172,13 +173,13 @@ LL | Foo::Z(_) => 1,
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms2.rs:206:9
+ --> $DIR/match_same_arms2.rs:207:9
|
LL | Foo::X(0) => 1,
| ^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms2.rs:231:9
+ --> $DIR/match_same_arms2.rs:232:9
|
LL | Some(Bar { y: 0, x: 5, .. }) => 1,
| ----------------------------^^^^^
@@ -187,13 +188,13 @@ LL | Some(Bar { y: 0, x: 5, .. }) => 1,
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms2.rs:228:9
+ --> $DIR/match_same_arms2.rs:229:9
|
LL | Some(Bar { x: 0, y: 5, .. }) => 1,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
- --> $DIR/match_same_arms2.rs:245:9
+ --> $DIR/match_same_arms2.rs:246:9
|
LL | 1 => cfg!(not_enable),
| -^^^^^^^^^^^^^^^^^^^^
@@ -202,7 +203,7 @@ LL | 1 => cfg!(not_enable),
|
= help: or try changing either arm body
note: other arm here
- --> $DIR/match_same_arms2.rs:244:9
+ --> $DIR/match_same_arms2.rs:245:9
|
LL | 0 => cfg!(not_enable),
| ^^^^^^^^^^^^^^^^^^^^^
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
new file mode 100644
index 000000000..07421173a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
@@ -0,0 +1,58 @@
+#![feature(non_exhaustive_omitted_patterns_lint)]
+#![warn(clippy::match_same_arms)]
+#![no_main]
+
+use std::sync::atomic::Ordering; // #[non_exhaustive] enum
+
+pub fn f(x: Ordering) {
+ 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!(),
+ }
+}
+
+mod f {
+ #![deny(non_exhaustive_omitted_patterns)]
+
+ use super::*;
+
+ pub fn f(x: Ordering) {
+ match x {
+ Ordering::Relaxed => println!("relaxed"),
+ Ordering::Release => println!("release"),
+ Ordering::Acquire => println!("acquire"),
+ Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ _ => panic!(),
+ }
+ }
+}
+
+// Below should still lint
+
+pub fn g(x: Ordering) {
+ match x {
+ Ordering::Relaxed => println!("relaxed"),
+ Ordering::Release => println!("release"),
+ Ordering::Acquire => println!("acquire"),
+ Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ _ => panic!(),
+ }
+}
+
+mod g {
+ use super::*;
+
+ pub fn g(x: Ordering) {
+ match x {
+ Ordering::Relaxed => println!("relaxed"),
+ Ordering::Release => println!("release"),
+ Ordering::Acquire => println!("acquire"),
+ Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ _ => panic!(),
+ }
+ }
+}
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
new file mode 100644
index 000000000..088f7d5c0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
@@ -0,0 +1,29 @@
+error: this match arm has an identical body to the `_` wildcard arm
+ --> $DIR/match_same_arms_non_exhaustive.rs:41:9
+ |
+LL | Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
+ |
+ = help: or try changing either arm body
+note: `_` wildcard arm here
+ --> $DIR/match_same_arms_non_exhaustive.rs:42:9
+ |
+LL | _ => panic!(),
+ | ^^^^^^^^^^^^^
+ = note: `-D clippy::match-same-arms` implied by `-D warnings`
+
+error: this match arm has an identical body to the `_` wildcard arm
+ --> $DIR/match_same_arms_non_exhaustive.rs:54:13
+ |
+LL | Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
+ |
+ = help: or try changing either arm body
+note: `_` wildcard arm here
+ --> $DIR/match_same_arms_non_exhaustive.rs:55:13
+ |
+LL | _ => panic!(),
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
index 7c29bb08e..f59ff456b 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -5,7 +5,8 @@
clippy::let_unit_value,
clippy::no_effect,
clippy::toplevel_ref_arg,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
struct Point {
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
index c068d5e17..e293bc33c 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.rs
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -5,7 +5,8 @@
clippy::let_unit_value,
clippy::no_effect,
clippy::toplevel_ref_arg,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
struct Point {
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
index 9d16af76c..8998786de 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -1,5 +1,5 @@
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:33:5
+ --> $DIR/match_single_binding.rs:34:5
|
LL | / match (a, b, c) {
LL | | (x, y, z) => {
@@ -18,7 +18,7 @@ LL + }
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:39:5
+ --> $DIR/match_single_binding.rs:40:5
|
LL | / match (a, b, c) {
LL | | (x, y, z) => println!("{} {} {}", x, y, z),
@@ -32,7 +32,7 @@ LL + println!("{} {} {}", x, y, z);
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:56:5
+ --> $DIR/match_single_binding.rs:57:5
|
LL | / match a {
LL | | _ => println!("whatever"),
@@ -40,7 +40,7 @@ LL | | }
| |_____^ help: consider using the match body instead: `println!("whatever");`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:60:5
+ --> $DIR/match_single_binding.rs:61:5
|
LL | / match a {
LL | | _ => {
@@ -59,7 +59,7 @@ LL + }
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:67:5
+ --> $DIR/match_single_binding.rs:68:5
|
LL | / match a {
LL | | _ => {
@@ -81,7 +81,7 @@ LL + }
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:77:5
+ --> $DIR/match_single_binding.rs:78:5
|
LL | / match p {
LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
@@ -95,7 +95,7 @@ LL + println!("Coords: ({}, {})", x, y);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:81:5
+ --> $DIR/match_single_binding.rs:82:5
|
LL | / match p {
LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
@@ -109,7 +109,7 @@ LL + println!("Coords: ({}, {})", x1, y1);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:86:5
+ --> $DIR/match_single_binding.rs:87:5
|
LL | / match x {
LL | | ref r => println!("Got a reference to {}", r),
@@ -123,7 +123,7 @@ LL + println!("Got a reference to {}", r);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:91:5
+ --> $DIR/match_single_binding.rs:92:5
|
LL | / match x {
LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
@@ -137,7 +137,7 @@ LL + println!("Got a mutable reference to {}", mr);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:95:5
+ --> $DIR/match_single_binding.rs:96:5
|
LL | / let product = match coords() {
LL | | Point { x, y } => x * y,
@@ -151,7 +151,7 @@ LL + let product = x * y;
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:103:18
+ --> $DIR/match_single_binding.rs:104:18
|
LL | .map(|i| match i.unwrap() {
| __________________^
@@ -168,7 +168,7 @@ LL ~ })
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:129:5
+ --> $DIR/match_single_binding.rs:130:5
|
LL | / match x {
LL | | // =>
@@ -177,7 +177,7 @@ LL | | }
| |_____^ help: consider using the match body instead: `println!("Not an array index start")`
error: this assignment could be simplified
- --> $DIR/match_single_binding.rs:138:5
+ --> $DIR/match_single_binding.rs:139:5
|
LL | / val = match val.split_at(idx) {
LL | | (pre, suf) => {
@@ -197,7 +197,7 @@ LL ~ };
|
error: this match could be replaced by its scrutinee and body
- --> $DIR/match_single_binding.rs:151:16
+ --> $DIR/match_single_binding.rs:152:16
|
LL | let _ = || match side_effects() {
| ________________^
@@ -214,7 +214,7 @@ LL ~ };
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:157:5
+ --> $DIR/match_single_binding.rs:158:5
|
LL | / match r {
LL | | x => match x {
@@ -239,7 +239,7 @@ LL ~ };
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:170:5
+ --> $DIR/match_single_binding.rs:171:5
|
LL | / match 1 {
LL | | _ => (),
@@ -247,7 +247,7 @@ LL | | }
| |_____^ help: consider using the match body instead: `();`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:174:13
+ --> $DIR/match_single_binding.rs:175:13
|
LL | let a = match 1 {
| _____________^
@@ -256,7 +256,7 @@ LL | | };
| |_____^ help: consider using the match body instead: `()`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:178:5
+ --> $DIR/match_single_binding.rs:179:5
|
LL | / match 1 {
LL | | _ => side_effects(),
@@ -264,7 +264,7 @@ LL | | }
| |_____^ help: consider using the match body instead: `side_effects();`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:182:13
+ --> $DIR/match_single_binding.rs:183:13
|
LL | let b = match 1 {
| _____________^
@@ -273,7 +273,7 @@ LL | | };
| |_____^ help: consider using the match body instead: `side_effects()`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:186:5
+ --> $DIR/match_single_binding.rs:187:5
|
LL | / match 1 {
LL | | _ => println!("1"),
@@ -281,7 +281,7 @@ LL | | }
| |_____^ help: consider using the match body instead: `println!("1");`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:190:13
+ --> $DIR/match_single_binding.rs:191:13
|
LL | let c = match 1 {
| _____________^
@@ -290,7 +290,7 @@ LL | | };
| |_____^ help: consider using the match body instead: `println!("1")`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:195:9
+ --> $DIR/match_single_binding.rs:196:9
|
LL | / match 1 {
LL | | _ => (),
@@ -298,7 +298,7 @@ LL | | },
| |_________^ help: consider using the match body instead: `()`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:198:9
+ --> $DIR/match_single_binding.rs:199:9
|
LL | / match 1 {
LL | | _ => side_effects(),
@@ -306,7 +306,7 @@ LL | | },
| |_________^ help: consider using the match body instead: `side_effects()`
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:201:9
+ --> $DIR/match_single_binding.rs:202:9
|
LL | / match 1 {
LL | | _ => println!("1"),
diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.rs b/src/tools/clippy/tests/ui/match_wild_err_arm.rs
index 823be65ef..5a552e4ae 100644
--- a/src/tools/clippy/tests/ui/match_wild_err_arm.rs
+++ b/src/tools/clippy/tests/ui/match_wild_err_arm.rs
@@ -1,7 +1,20 @@
#![feature(exclusive_range_pattern)]
-#![allow(clippy::match_same_arms)]
+#![allow(clippy::match_same_arms, dead_code)]
#![warn(clippy::match_wild_err_arm)]
+fn issue_10635() {
+ enum Error {
+ A,
+ B,
+ }
+
+ // Don't trigger in const contexts. Const unwrap is not yet stable
+ const X: () = match Ok::<_, Error>(()) {
+ Ok(x) => x,
+ Err(_) => panic!(),
+ };
+}
+
fn match_wild_err_arm() {
let x: Result<i32, &str> = Ok(3);
diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr
index b016d6826..a9f54feac 100644
--- a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr
+++ b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr
@@ -1,5 +1,5 @@
error: `Err(_)` matches all errors
- --> $DIR/match_wild_err_arm.rs:11:9
+ --> $DIR/match_wild_err_arm.rs:24:9
|
LL | Err(_) => panic!("err"),
| ^^^^^^
@@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"),
= note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
error: `Err(_)` matches all errors
- --> $DIR/match_wild_err_arm.rs:17:9
+ --> $DIR/match_wild_err_arm.rs:30:9
|
LL | Err(_) => panic!(),
| ^^^^^^
@@ -16,7 +16,7 @@ LL | Err(_) => panic!(),
= note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
error: `Err(_)` matches all errors
- --> $DIR/match_wild_err_arm.rs:23:9
+ --> $DIR/match_wild_err_arm.rs:36:9
|
LL | Err(_) => {
| ^^^^^^
@@ -24,7 +24,7 @@ LL | Err(_) => {
= note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
error: `Err(_e)` matches all errors
- --> $DIR/match_wild_err_arm.rs:31:9
+ --> $DIR/match_wild_err_arm.rs:44:9
|
LL | Err(_e) => panic!(),
| ^^^^^^^
diff --git a/src/tools/clippy/tests/ui/mem_forget.rs b/src/tools/clippy/tests/ui/mem_forget.rs
index edb9d87d0..b6c8d9e53 100644
--- a/src/tools/clippy/tests/ui/mem_forget.rs
+++ b/src/tools/clippy/tests/ui/mem_forget.rs
@@ -19,5 +19,8 @@ fn main() {
let eight: Vec<i32> = vec![8];
forgetSomething(eight);
+ let string = String::new();
+ std::mem::forget(string);
+
std::mem::forget(7);
}
diff --git a/src/tools/clippy/tests/ui/mem_forget.stderr b/src/tools/clippy/tests/ui/mem_forget.stderr
index a90d8b165..8004b2aa8 100644
--- a/src/tools/clippy/tests/ui/mem_forget.stderr
+++ b/src/tools/clippy/tests/ui/mem_forget.stderr
@@ -4,6 +4,7 @@ error: usage of `mem::forget` on `Drop` type
LL | memstuff::forget(six);
| ^^^^^^^^^^^^^^^^^^^^^
|
+ = note: argument has type `std::sync::Arc<i32>`
= note: `-D clippy::mem-forget` implied by `-D warnings`
error: usage of `mem::forget` on `Drop` type
@@ -11,12 +12,24 @@ error: usage of `mem::forget` on `Drop` type
|
LL | std::mem::forget(seven);
| ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: argument has type `std::rc::Rc<i32>`
error: usage of `mem::forget` on `Drop` type
--> $DIR/mem_forget.rs:20:5
|
LL | forgetSomething(eight);
| ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: argument has type `std::vec::Vec<i32>`
+
+error: usage of `mem::forget` on type with `Drop` fields
+ --> $DIR/mem_forget.rs:23:5
+ |
+LL | std::mem::forget(string);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: argument has type `std::string::String`
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.rs b/src/tools/clippy/tests/ui/mem_replace_macro.rs
index 132873858..e53342f2e 100644
--- a/src/tools/clippy/tests/ui/mem_replace_macro.rs
+++ b/src/tools/clippy/tests/ui/mem_replace_macro.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::mem_replace_with_default)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs
index e0e2cac30..589eab5cd 100644
--- a/src/tools/clippy/tests/ui/methods.rs
+++ b/src/tools/clippy/tests/ui/methods.rs
@@ -18,6 +18,7 @@
clippy::wrong_self_convention,
clippy::unused_async,
clippy::unused_self,
+ clippy::useless_vec,
unused
)]
diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr
index 4643e09e2..73ec48643 100644
--- a/src/tools/clippy/tests/ui/methods.stderr
+++ b/src/tools/clippy/tests/ui/methods.stderr
@@ -1,5 +1,5 @@
error: methods called `new` usually return `Self`
- --> $DIR/methods.rs:105:5
+ --> $DIR/methods.rs:106:5
|
LL | / fn new() -> i32 {
LL | | 0
@@ -9,7 +9,7 @@ LL | | }
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
- --> $DIR/methods.rs:126:13
+ --> $DIR/methods.rs:127:13
|
LL | let _ = v.iter().filter(|&x| {
| _____________^
diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed
index dcbed5a4d..ce5d19a8b 100644
--- a/src/tools/clippy/tests/ui/methods_fixable.fixed
+++ b/src/tools/clippy/tests/ui/methods_fixable.fixed
@@ -1,6 +1,7 @@
//@run-rustfix
#![warn(clippy::filter_next)]
+#![allow(clippy::useless_vec)]
/// Checks implementation of `FILTER_NEXT` lint.
fn main() {
diff --git a/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs
index 3a976d235..0615817ec 100644
--- a/src/tools/clippy/tests/ui/methods_fixable.rs
+++ b/src/tools/clippy/tests/ui/methods_fixable.rs
@@ -1,6 +1,7 @@
//@run-rustfix
#![warn(clippy::filter_next)]
+#![allow(clippy::useless_vec)]
/// Checks implementation of `FILTER_NEXT` lint.
fn main() {
diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr
index 852f48e32..187714c75 100644
--- a/src/tools/clippy/tests/ui/methods_fixable.stderr
+++ b/src/tools/clippy/tests/ui/methods_fixable.stderr
@@ -1,5 +1,5 @@
error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
- --> $DIR/methods_fixable.rs:10:13
+ --> $DIR/methods_fixable.rs:11:13
|
LL | let _ = v.iter().filter(|&x| *x < 0).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)`
diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs
new file mode 100644
index 000000000..b5b9e66aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_ident_chars.rs
@@ -0,0 +1,84 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(irrefutable_let_patterns, nonstandard_style, unused)]
+#![warn(clippy::min_ident_chars)]
+
+extern crate proc_macros;
+use proc_macros::external;
+use proc_macros::with_span;
+
+struct A {
+ a: u32,
+ i: u32,
+ A: u32,
+ I: u32,
+}
+
+struct B(u32);
+
+struct O {
+ o: u32,
+}
+
+struct i;
+
+enum C {
+ D,
+ E,
+ F,
+ j,
+}
+
+struct Vec4 {
+ x: u32,
+ y: u32,
+ z: u32,
+ w: u32,
+}
+
+struct AA<T, E>(T, E);
+
+fn main() {
+ // Allowed idents
+ let w = 1;
+ // Ok, not this one
+ // let i = 1;
+ let j = 1;
+ let n = 1;
+ let z = 1;
+ let y = 1;
+ let z = 1;
+ // Implicitly disallowed idents
+ let h = 1;
+ let e = 2;
+ let l = 3;
+ let l = 4;
+ let o = 6;
+ // 2 len does not lint
+ let hi = 0;
+ // Lint
+ let (h, o, w) = (1, 2, 3);
+ for (a, (r, e)) in (0..1000).enumerate().enumerate() {}
+ let you = Vec4 { x: 1, y: 2, z: 3, w: 4 };
+ while let (d, o, _i, n, g) = (true, true, false, false, true) {}
+ let today = true;
+ // Ideally this wouldn't lint, but this would (likely) require global analysis, outta scope
+ // of this lint regardless
+ let o = 1;
+ let o = O { o };
+
+ for j in 0..1000 {}
+ for _ in 0..10 {}
+
+ // Do not lint code from external macros
+ external! { for j in 0..1000 {} }
+ // Do not lint code from procedural macros
+ with_span! {
+ span
+ for j in 0..1000 {}
+ }
+}
+
+fn b() {}
+fn wrong_pythagoras(a: f32, b: f32) -> f32 {
+ a * a + a * b
+}
diff --git a/src/tools/clippy/tests/ui/min_ident_chars.stderr b/src/tools/clippy/tests/ui/min_ident_chars.stderr
new file mode 100644
index 000000000..66a63f657
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_ident_chars.stderr
@@ -0,0 +1,178 @@
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:9:8
+ |
+LL | struct A {
+ | ^
+ |
+ = note: `-D clippy::min-ident-chars` implied by `-D warnings`
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:10:5
+ |
+LL | a: u32,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:12:5
+ |
+LL | A: u32,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:13:5
+ |
+LL | I: u32,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:16:8
+ |
+LL | struct B(u32);
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:18:8
+ |
+LL | struct O {
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:19:5
+ |
+LL | o: u32,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:24:6
+ |
+LL | enum C {
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:25:5
+ |
+LL | D,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:26:5
+ |
+LL | E,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:27:5
+ |
+LL | F,
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:51:9
+ |
+LL | let h = 1;
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:52:9
+ |
+LL | let e = 2;
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:53:9
+ |
+LL | let l = 3;
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:54:9
+ |
+LL | let l = 4;
+ | ^
+
+error: this ident consists of a single char
+ --> $DIR/min_ident_chars.rs:55:9
+ |
+LL | let o = 6;
+ | ^
+
+error: this ident consists of a single char
+ --> $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:59:13
+ |
+LL | let (h, o, w) = (1, 2, 3);
+ | ^
+
+error: this ident consists of a single char
+ --> $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: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: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: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: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: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:66:9
+ |
+LL | let o = 1;
+ | ^
+
+error: this ident consists of a single char
+ --> $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:81:4
+ |
+LL | fn b() {}
+ | ^
+
+error: this ident consists of a single char
+ --> $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:82:29
+ |
+LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 {
+ | ^
+
+error: aborting due to 29 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs
index 89404ca88..af1358f61 100644
--- a/src/tools/clippy/tests/ui/missing_assert_message.rs
+++ b/src/tools/clippy/tests/ui/missing_assert_message.rs
@@ -7,8 +7,6 @@ macro_rules! bar {
};
}
-fn main() {}
-
// Should trigger warning
fn asserts_without_message() {
assert!(foo());
@@ -66,9 +64,14 @@ fn asserts_without_message_but_inside_a_test_function() {
debug_assert_ne!(foo(), foo());
}
+fn foo() -> bool {
+ true
+}
+
// Should not trigger warning
#[cfg(test)]
mod tests {
+ use super::foo;
fn asserts_without_message_but_inside_a_test_module() {
assert!(foo());
assert_eq!(foo(), foo());
@@ -78,7 +81,3 @@ mod tests {
debug_assert_ne!(foo(), foo());
}
}
-
-fn foo() -> bool {
- true
-}
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr
index ecd038012..33a5c1f8e 100644
--- a/src/tools/clippy/tests/ui/missing_assert_message.stderr
+++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr
@@ -1,5 +1,5 @@
error: assert without any message
- --> $DIR/missing_assert_message.rs:14:5
+ --> $DIR/missing_assert_message.rs:12:5
|
LL | assert!(foo());
| ^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | assert!(foo());
= note: `-D clippy::missing-assert-message` implied by `-D warnings`
error: assert without any message
- --> $DIR/missing_assert_message.rs:15:5
+ --> $DIR/missing_assert_message.rs:13:5
|
LL | assert_eq!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | assert_eq!(foo(), foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:16:5
+ --> $DIR/missing_assert_message.rs:14:5
|
LL | assert_ne!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | assert_ne!(foo(), foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:17:5
+ --> $DIR/missing_assert_message.rs:15:5
|
LL | debug_assert!(foo());
| ^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL | debug_assert!(foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:18:5
+ --> $DIR/missing_assert_message.rs:16:5
|
LL | debug_assert_eq!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL | debug_assert_eq!(foo(), foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:19:5
+ --> $DIR/missing_assert_message.rs:17:5
|
LL | debug_assert_ne!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL | debug_assert_ne!(foo(), foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:24:5
+ --> $DIR/missing_assert_message.rs:22:5
|
LL | assert!(bar!(true));
| ^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL | assert!(bar!(true));
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:25:5
+ --> $DIR/missing_assert_message.rs:23:5
|
LL | assert!(bar!(true, false));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL | assert!(bar!(true, false));
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:26:5
+ --> $DIR/missing_assert_message.rs:24:5
|
LL | assert_eq!(bar!(true), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL | assert_eq!(bar!(true), foo());
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:27:5
+ --> $DIR/missing_assert_message.rs:25:5
|
LL | assert_ne!(bar!(true, true), bar!(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL | assert_ne!(bar!(true, true), bar!(true));
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:32:5
+ --> $DIR/missing_assert_message.rs:30:5
|
LL | assert!(foo(),);
| ^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | assert!(foo(),);
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:33:5
+ --> $DIR/missing_assert_message.rs:31:5
|
LL | assert_eq!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL | assert_eq!(foo(), foo(),);
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:34:5
+ --> $DIR/missing_assert_message.rs:32:5
|
LL | assert_ne!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,7 +104,7 @@ LL | assert_ne!(foo(), foo(),);
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:35:5
+ --> $DIR/missing_assert_message.rs:33:5
|
LL | debug_assert!(foo(),);
| ^^^^^^^^^^^^^^^^^^^^^
@@ -112,7 +112,7 @@ LL | debug_assert!(foo(),);
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:36:5
+ --> $DIR/missing_assert_message.rs:34:5
|
LL | debug_assert_eq!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL | debug_assert_eq!(foo(), foo(),);
= help: consider describing why the failing assert is problematic
error: assert without any message
- --> $DIR/missing_assert_message.rs:37:5
+ --> $DIR/missing_assert_message.rs:35:5
|
LL | debug_assert_ne!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index 5db73a7b8..06e053524 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -3,7 +3,7 @@
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
//@aux-build:helper.rs
-//@aux-build:../../auxiliary/proc_macros.rs
+//@aux-build:../auxiliary/proc_macros.rs:proc-macro
#![warn(clippy::missing_const_for_fn)]
#![feature(start)]
@@ -13,7 +13,7 @@ extern crate proc_macros;
use proc_macros::with_span;
-struct Game;
+struct Game; // You just lost.
// This should not be linted because it's already const
const fn already_const() -> i32 {
@@ -44,7 +44,6 @@ static Y: u32 = 0;
// refer to a static variable
fn get_y() -> u32 {
Y
- //~^ ERROR E0013
}
// Don't lint entrypoint functions
@@ -126,3 +125,43 @@ with_span! {
span
fn dont_check_in_proc_macro() {}
}
+
+// Do not lint `String` has `Vec<u8>`, which cannot be dropped in const contexts
+fn a(this: String) {}
+
+enum A {
+ F(String),
+ N,
+}
+
+// Same here.
+fn b(this: A) {}
+
+// Minimized version of `a`.
+fn c(this: Vec<u16>) {}
+
+struct F(A);
+
+// Do not lint
+fn f(this: F) {}
+
+// Do not lint
+fn g<T>(this: T) {}
+
+struct Issue10617(String);
+
+impl Issue10617 {
+ // Do not lint
+ pub fn name(self) -> String {
+ self.0
+ }
+}
+
+union U {
+ f: u32,
+}
+
+// Do not lint because accessing union fields from const functions is unstable
+fn h(u: U) -> u32 {
+ unsafe { u.f }
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index 0246c8622..b1980b1b5 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,5 +1,7 @@
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return)]
+#![feature(const_mut_refs)]
+#![feature(const_trait_impl)]
use std::mem::transmute;
@@ -87,3 +89,14 @@ fn msrv_1_46() -> i32 {
// Should not be const
fn main() {}
+
+struct D;
+
+impl const Drop for D {
+ fn drop(&mut self) {
+ todo!();
+ }
+}
+
+// Lint this, since it can be dropped in const contexts
+fn d(this: D) {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index 955e1ed26..7be2cc0ca 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -1,5 +1,5 @@
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:12:5
+ --> $DIR/could_be_const.rs:14:5
|
LL | / pub fn new() -> Self {
LL | | Self { guess: 42 }
@@ -9,7 +9,7 @@ LL | | }
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:16:5
+ --> $DIR/could_be_const.rs:18:5
|
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
LL | | b
@@ -17,7 +17,7 @@ LL | | }
| |_____^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:22:1
+ --> $DIR/could_be_const.rs:24:1
|
LL | / fn one() -> i32 {
LL | | 1
@@ -25,7 +25,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:27:1
+ --> $DIR/could_be_const.rs:29:1
|
LL | / fn two() -> i32 {
LL | | let abc = 2;
@@ -34,7 +34,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:33:1
+ --> $DIR/could_be_const.rs:35:1
|
LL | / fn string() -> String {
LL | | String::new()
@@ -42,7 +42,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:38:1
+ --> $DIR/could_be_const.rs:40:1
|
LL | / unsafe fn four() -> i32 {
LL | | 4
@@ -50,7 +50,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:43:1
+ --> $DIR/could_be_const.rs:45:1
|
LL | / fn generic<T>(t: T) -> T {
LL | | t
@@ -58,7 +58,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:51:1
+ --> $DIR/could_be_const.rs:53:1
|
LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
LL | | t[0]
@@ -66,7 +66,7 @@ LL | | }
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:64:9
+ --> $DIR/could_be_const.rs:66:9
|
LL | / pub fn b(self, a: &A) -> B {
LL | | B
@@ -74,7 +74,7 @@ LL | | }
| |_________^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:73:5
+ --> $DIR/could_be_const.rs:75:5
|
LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
LL | | byte.is_ascii_digit();
@@ -82,12 +82,18 @@ LL | | }
| |_____^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:84:1
+ --> $DIR/could_be_const.rs:86:1
|
LL | / fn msrv_1_46() -> i32 {
LL | | 46
LL | | }
| |_^
-error: aborting due to 11 previous errors
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:102:1
+ |
+LL | fn d(this: D) {}
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
diff --git a/src/tools/clippy/tests/ui/missing_doc.rs b/src/tools/clippy/tests/ui/missing_doc.rs
index bf587e774..cff1706a8 100644
--- a/src/tools/clippy/tests/ui/missing_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_doc.rs
@@ -1,5 +1,5 @@
//@needs-asm-support
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::missing_docs_in_private_items)]
// When denying at the crate level, be sure to not get random warnings from the
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.rs b/src/tools/clippy/tests/ui/missing_doc_impl.rs
index 520ddbe16..2d45132f9 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.rs
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.rs
@@ -1,4 +1,4 @@
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::missing_docs_in_private_items)]
#![allow(dead_code)]
diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs
new file mode 100644
index 000000000..c156d394e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs
@@ -0,0 +1,191 @@
+#![allow(unused)]
+#![warn(clippy::missing_fields_in_debug)]
+
+use std::fmt;
+use std::marker::PhantomData;
+use std::ops::Deref;
+
+struct NamedStruct1Ignored {
+ data: u8,
+ hidden: u32,
+}
+
+impl fmt::Debug for NamedStruct1Ignored {
+ // unused field: hidden
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter
+ .debug_struct("NamedStruct1Ignored")
+ .field("data", &self.data)
+ .finish()
+ }
+}
+
+struct NamedStructMultipleIgnored {
+ data: u8,
+ hidden: u32,
+ hidden2: String,
+ hidden3: Vec<Vec<i32>>,
+ hidden4: ((((u8), u16), u32), u64),
+}
+
+impl fmt::Debug for NamedStructMultipleIgnored {
+ // unused fields: hidden, hidden2, hidden4
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter
+ .debug_struct("NamedStructMultipleIgnored")
+ .field("data", &self.data)
+ .field("hidden3", &self.hidden3)
+ .finish()
+ }
+}
+
+struct Unit;
+
+// ok
+impl fmt::Debug for Unit {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.debug_struct("Unit").finish()
+ }
+}
+
+struct UnnamedStruct1Ignored(String);
+
+impl fmt::Debug for UnnamedStruct1Ignored {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.debug_tuple("UnnamedStruct1Ignored").finish()
+ }
+}
+
+struct UnnamedStructMultipleIgnored(String, Vec<u8>, i32);
+
+// tuple structs are not linted
+impl fmt::Debug for UnnamedStructMultipleIgnored {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter
+ .debug_tuple("UnnamedStructMultipleIgnored")
+ .field(&self.1)
+ .finish()
+ }
+}
+
+struct NamedStructNonExhaustive {
+ a: u8,
+ b: String,
+}
+
+// ok
+impl fmt::Debug for NamedStructNonExhaustive {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter
+ .debug_struct("NamedStructNonExhaustive")
+ .field("a", &self.a)
+ .finish_non_exhaustive() // should not warn here
+ }
+}
+
+struct MultiExprDebugImpl {
+ a: u8,
+ b: String,
+}
+
+// ok
+impl fmt::Debug for MultiExprDebugImpl {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = formatter.debug_struct("MultiExprDebugImpl");
+ f.field("a", &self.a);
+ f.finish()
+ }
+}
+
+#[derive(Debug)]
+struct DerivedStruct {
+ a: u8,
+ b: i32,
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1166846953
+
+struct Inner {
+ a: usize,
+ b: usize,
+}
+
+struct HasInner {
+ inner: Inner,
+}
+
+impl HasInner {
+ fn get(&self) -> &Inner {
+ &self.inner
+ }
+}
+
+impl fmt::Debug for HasInner {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let inner = self.get();
+
+ f.debug_struct("HasInner")
+ .field("a", &inner.a)
+ .field("b", &inner.b)
+ .finish()
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1170306053
+struct Foo {
+ a: u8,
+ b: u8,
+}
+
+impl fmt::Debug for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Foo").field("a", &self.a).field("b", &()).finish()
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175473620
+mod comment1175473620 {
+ use super::*;
+
+ struct Inner {
+ a: usize,
+ b: usize,
+ }
+ struct Wrapper(Inner);
+
+ impl Deref for Wrapper {
+ type Target = Inner;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl fmt::Debug for Wrapper {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Wrapper")
+ .field("a", &self.a)
+ .field("b", &self.b)
+ .finish()
+ }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175488757
+// PhantomData is an exception and does not need to be included
+struct WithPD {
+ a: u8,
+ b: u8,
+ c: PhantomData<String>,
+}
+
+impl fmt::Debug for WithPD {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("WithPD")
+ .field("a", &self.a)
+ .field("b", &self.b)
+ .finish()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr
new file mode 100644
index 000000000..ef9d02aba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr
@@ -0,0 +1,73 @@
+error: manual `Debug` impl does not include all fields
+ --> $DIR/missing_fields_in_debug.rs:13:1
+ |
+LL | / impl fmt::Debug for NamedStruct1Ignored {
+LL | | // unused field: hidden
+LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+LL | | formatter
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+note: this field is unused
+ --> $DIR/missing_fields_in_debug.rs:10:5
+ |
+LL | hidden: u32,
+ | ^^^^^^^^^^^
+ = help: consider including all fields in this `Debug` impl
+ = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
+ = note: `-D clippy::missing-fields-in-debug` implied by `-D warnings`
+
+error: manual `Debug` impl does not include all fields
+ --> $DIR/missing_fields_in_debug.rs:31:1
+ |
+LL | / impl fmt::Debug for NamedStructMultipleIgnored {
+LL | | // unused fields: hidden, hidden2, hidden4
+LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+LL | | formatter
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+note: this field is unused
+ --> $DIR/missing_fields_in_debug.rs:25:5
+ |
+LL | hidden: u32,
+ | ^^^^^^^^^^^
+note: this field is unused
+ --> $DIR/missing_fields_in_debug.rs:26:5
+ |
+LL | hidden2: String,
+ | ^^^^^^^^^^^^^^^
+note: this field is unused
+ --> $DIR/missing_fields_in_debug.rs:28:5
+ |
+LL | hidden4: ((((u8), u16), u32), u64),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider including all fields in this `Debug` impl
+ = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
+
+error: manual `Debug` impl does not include all fields
+ --> $DIR/missing_fields_in_debug.rs:92:1
+ |
+LL | / impl fmt::Debug for MultiExprDebugImpl {
+LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+LL | | let mut f = formatter.debug_struct("MultiExprDebugImpl");
+LL | | f.field("a", &self.a);
+LL | | f.finish()
+LL | | }
+LL | | }
+ | |_^
+ |
+note: this field is unused
+ --> $DIR/missing_fields_in_debug.rs:88:5
+ |
+LL | b: String,
+ | ^^^^^^^^^
+ = help: consider including all fields in this `Debug` impl
+ = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs
index 3c68fb905..e47a198c6 100644
--- a/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs
+++ b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs
@@ -1,5 +1,4 @@
#![warn(clippy::missing_inline_in_public_items)]
-#![crate_type = "proc-macro"]
extern crate proc_macro;
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs
index 7dc445292..0e1533fc1 100644
--- a/src/tools/clippy/tests/ui/missing_panics_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs
@@ -1,5 +1,12 @@
+//@aux-build:macro_rules.rs
#![warn(clippy::missing_panics_doc)]
-#![allow(clippy::option_map_unit_fn)]
+#![allow(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)]
+
+#[macro_use]
+extern crate macro_rules;
+
+use macro_rules::macro_with_panic;
+
fn main() {}
/// This needs to be documented
@@ -14,11 +21,6 @@ pub fn panic() {
}
/// This needs to be documented
-pub fn todo() {
- todo!()
-}
-
-/// This needs to be documented
pub fn inner_body(opt: Option<u32>) {
opt.map(|x| {
if x == 10 {
@@ -81,15 +83,6 @@ pub fn inner_body_documented(opt: Option<u32>) {
/// # Panics
///
/// We still need to do this part
-pub fn todo_documented() {
- todo!()
-}
-
-/// This is documented
-///
-/// # Panics
-///
-/// We still need to do this part
pub fn unreachable_amd_panic_documented() {
if true { unreachable!() } else { panic!() }
}
@@ -114,6 +107,11 @@ pub fn assert_ne_documented() {
assert_ne!(x, 0);
}
+/// `todo!()` is fine
+pub fn todo() {
+ todo!()
+}
+
/// This is okay because it is private
fn unwrap_private() {
let result = Err("Hi");
@@ -126,11 +124,6 @@ fn panic_private() {
}
/// This is okay because it is private
-fn todo_private() {
- todo!()
-}
-
-/// This is okay because it is private
fn inner_body_private(opt: Option<u32>) {
opt.map(|x| {
if x == 10 {
@@ -151,3 +144,50 @@ pub fn debug_assertions() {
debug_assert_eq!(1, 2);
debug_assert_ne!(1, 2);
}
+
+// all function must be triggered the lint.
+// `pub` is required, because the lint does not consider unreachable items
+pub mod issue10240 {
+ pub fn option_unwrap<T>(v: &[T]) -> &T {
+ let o: Option<&T> = v.last();
+ o.unwrap()
+ }
+
+ pub fn option_expect<T>(v: &[T]) -> &T {
+ let o: Option<&T> = v.last();
+ o.expect("passed an empty thing")
+ }
+
+ pub fn result_unwrap<T>(v: &[T]) -> &T {
+ let res: Result<&T, &str> = v.last().ok_or("oh noes");
+ res.unwrap()
+ }
+
+ pub fn result_expect<T>(v: &[T]) -> &T {
+ let res: Result<&T, &str> = v.last().ok_or("oh noes");
+ res.expect("passed an empty thing")
+ }
+
+ pub fn last_unwrap(v: &[u32]) -> u32 {
+ *v.last().unwrap()
+ }
+
+ pub fn last_expect(v: &[u32]) -> u32 {
+ *v.last().expect("passed an empty thing")
+ }
+}
+
+fn from_external_macro_should_not_lint() {
+ macro_with_panic!()
+}
+
+macro_rules! some_macro_that_panics {
+ () => {
+ panic!()
+ };
+}
+
+fn from_declared_macro_should_lint_at_macrosite() {
+ // Not here.
+ some_macro_that_panics!()
+}
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
index 183c262ce..3dbe2dfbd 100644
--- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
@@ -1,87 +1,147 @@
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:6:1
+ --> $DIR/missing_panics_doc.rs:13:1
|
LL | pub fn unwrap() {
| ^^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:8:5
+ --> $DIR/missing_panics_doc.rs:15:5
|
LL | result.unwrap()
| ^^^^^^^^^^^^^^^
= note: `-D clippy::missing-panics-doc` implied by `-D warnings`
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:12:1
+ --> $DIR/missing_panics_doc.rs:19:1
|
LL | pub fn panic() {
| ^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:13:5
+ --> $DIR/missing_panics_doc.rs:20:5
|
LL | panic!("This function panics")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:17:1
- |
-LL | pub fn todo() {
- | ^^^^^^^^^^^^^
- |
-note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:18:5
- |
-LL | todo!()
- | ^^^^^^^
-
-error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:22:1
+ --> $DIR/missing_panics_doc.rs:24:1
|
LL | pub fn inner_body(opt: Option<u32>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:25:13
+ --> $DIR/missing_panics_doc.rs:27:13
|
LL | panic!()
| ^^^^^^^^
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:31:1
+ --> $DIR/missing_panics_doc.rs:33:1
|
LL | pub fn unreachable_and_panic() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:32:39
+ --> $DIR/missing_panics_doc.rs:34:39
|
LL | if true { unreachable!() } else { panic!() }
| ^^^^^^^^
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:36:1
+ --> $DIR/missing_panics_doc.rs:38:1
|
LL | pub fn assert_eq() {
| ^^^^^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:38:5
+ --> $DIR/missing_panics_doc.rs:40:5
|
LL | assert_eq!(x, 0);
| ^^^^^^^^^^^^^^^^
error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:42:1
+ --> $DIR/missing_panics_doc.rs:44:1
|
LL | pub fn assert_ne() {
| ^^^^^^^^^^^^^^^^^^
|
note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:44:5
+ --> $DIR/missing_panics_doc.rs:46:5
|
LL | assert_ne!(x, 0);
| ^^^^^^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:151:5
+ |
+LL | pub fn option_unwrap<T>(v: &[T]) -> &T {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:153:9
+ |
+LL | o.unwrap()
+ | ^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:156:5
+ |
+LL | pub fn option_expect<T>(v: &[T]) -> &T {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:158:9
+ |
+LL | o.expect("passed an empty thing")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:161:5
+ |
+LL | pub fn result_unwrap<T>(v: &[T]) -> &T {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:163:9
+ |
+LL | res.unwrap()
+ | ^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:166:5
+ |
+LL | pub fn result_expect<T>(v: &[T]) -> &T {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:168:9
+ |
+LL | res.expect("passed an empty thing")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:171:5
+ |
+LL | pub fn last_unwrap(v: &[u32]) -> u32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:172:10
+ |
+LL | *v.last().unwrap()
+ | ^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:175:5
+ |
+LL | pub fn last_expect(v: &[u32]) -> u32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:176:10
+ |
+LL | *v.last().expect("passed an empty thing")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
index 62cfeafdc..9c2ffcb02 100644
--- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(
dead_code,
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
index f83b7c3db..a0a1e96a7 100644
--- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![allow(
dead_code,
diff --git a/src/tools/clippy/tests/ui/module_inception.rs b/src/tools/clippy/tests/ui/module_inception.rs
index a23aba916..802c3ec39 100644
--- a/src/tools/clippy/tests/ui/module_inception.rs
+++ b/src/tools/clippy/tests/ui/module_inception.rs
@@ -1,5 +1,17 @@
#![warn(clippy::module_inception)]
+pub mod foo2 {
+ pub mod bar2 {
+ pub mod bar2 {
+ pub mod foo2 {}
+ }
+ pub mod foo2 {}
+ }
+ pub mod foo2 {
+ pub mod bar2 {}
+ }
+}
+
mod foo {
mod bar {
mod bar {
diff --git a/src/tools/clippy/tests/ui/module_inception.stderr b/src/tools/clippy/tests/ui/module_inception.stderr
index 77564dce9..ebb8e296f 100644
--- a/src/tools/clippy/tests/ui/module_inception.stderr
+++ b/src/tools/clippy/tests/ui/module_inception.stderr
@@ -1,8 +1,8 @@
error: module has the same name as its containing module
--> $DIR/module_inception.rs:5:9
|
-LL | / mod bar {
-LL | | mod foo {}
+LL | / pub mod bar2 {
+LL | | pub mod foo2 {}
LL | | }
| |_________^
|
@@ -11,10 +11,26 @@ LL | | }
error: module has the same name as its containing module
--> $DIR/module_inception.rs:10:5
|
+LL | / pub mod foo2 {
+LL | | pub mod bar2 {}
+LL | | }
+ | |_____^
+
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:17:9
+ |
+LL | / mod bar {
+LL | | mod foo {}
+LL | | }
+ | |_________^
+
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:22:5
+ |
LL | / mod foo {
LL | | mod bar {}
LL | | }
| |_____^
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr
index 04ecdef5e..83a76f81d 100644
--- a/src/tools/clippy/tests/ui/modulo_one.stderr
+++ b/src/tools/clippy/tests/ui/modulo_one.stderr
@@ -2,7 +2,7 @@ error: this operation will panic at runtime
--> $DIR/modulo_one.rs:11:5
|
LL | i32::MIN % (-1); // also caught by rustc
- | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow
+ | ^^^^^^^^^^^^^^^ attempt to compute `i32::MIN % -1_i32`, which would overflow
|
= note: `#[deny(unconditional_panic)]` on by default
@@ -10,13 +10,13 @@ error: this operation will panic at runtime
--> $DIR/modulo_one.rs:21:5
|
LL | INT_MIN % NEG_ONE; // also caught by rustc
- | ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
+ | ^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: this operation will panic at runtime
--> $DIR/modulo_one.rs:22:5
|
LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
- | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: any number modulo 1 will be 0
--> $DIR/modulo_one.rs:8:5
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..23ad36bb4 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
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(unused)]
#![allow(deref_nullptr)]
#![allow(clippy::unnecessary_operation)]
diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed
index 4f7cf4e56..c460fd7c6 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.fixed
+++ b/src/tools/clippy/tests/ui/must_use_unit.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::must_use_unit)]
#![allow(clippy::unused_unit)]
diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs
index 3a814ce16..fe95624f7 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.rs
+++ b/src/tools/clippy/tests/ui/must_use_unit.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::must_use_unit)]
#![allow(clippy::unused_unit)]
diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs
index d838098de..b72134283 100644
--- a/src/tools/clippy/tests/ui/mut_mut.rs
+++ b/src/tools/clippy/tests/ui/mut_mut.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::mut_mut)]
#![allow(unused)]
#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)]
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs
index 00871f9f4..321aa69a1 100644
--- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs
@@ -1,4 +1,7 @@
-//@aux-build:proc_macro_attr.rs
+//@aux-build:proc_macro_attr.rs:proc-macro
+// Flaky test, see https://github.com/rust-lang/rust/issues/113585.
+//@ignore-32bit
+//@ignore-64bit
#![warn(clippy::needless_arbitrary_self_type)]
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
index bf1911881..7d0e55652 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
@@ -7,6 +7,7 @@
clippy::no_effect,
clippy::if_same_then_else,
clippy::equatable_if_let,
+ clippy::needless_if,
clippy::needless_return,
clippy::self_named_constructors
)]
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
index a6c465d4f..88bfe8af7 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
@@ -7,6 +7,7 @@
clippy::no_effect,
clippy::if_same_then_else,
clippy::equatable_if_let,
+ clippy::needless_if,
clippy::needless_return,
clippy::self_named_constructors
)]
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
index fa906374f..1476aea43 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:41:5
+ --> $DIR/fixable.rs:42:5
|
LL | / if x {
LL | | true
@@ -11,7 +11,7 @@ LL | | };
= note: `-D clippy::needless-bool` implied by `-D warnings`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:46:5
+ --> $DIR/fixable.rs:47:5
|
LL | / if x {
LL | | false
@@ -21,7 +21,7 @@ LL | | };
| |_____^ help: you can reduce it to: `!x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:51:5
+ --> $DIR/fixable.rs:52:5
|
LL | / if x && y {
LL | | false
@@ -31,7 +31,7 @@ LL | | };
| |_____^ help: you can reduce it to: `!(x && y)`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:59:5
+ --> $DIR/fixable.rs:60:5
|
LL | / if a == b {
LL | | false
@@ -41,7 +41,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a != b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:64:5
+ --> $DIR/fixable.rs:65:5
|
LL | / if a != b {
LL | | false
@@ -51,7 +51,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a == b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:69:5
+ --> $DIR/fixable.rs:70:5
|
LL | / if a < b {
LL | | false
@@ -61,7 +61,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a >= b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:74:5
+ --> $DIR/fixable.rs:75:5
|
LL | / if a <= b {
LL | | false
@@ -71,7 +71,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a > b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:79:5
+ --> $DIR/fixable.rs:80:5
|
LL | / if a > b {
LL | | false
@@ -81,7 +81,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a <= b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:84:5
+ --> $DIR/fixable.rs:85:5
|
LL | / if a >= b {
LL | | false
@@ -91,7 +91,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a < b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:112:5
+ --> $DIR/fixable.rs:113:5
|
LL | / if x {
LL | | return true;
@@ -101,7 +101,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:120:5
+ --> $DIR/fixable.rs:121:5
|
LL | / if x {
LL | | return false;
@@ -111,7 +111,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:128:5
+ --> $DIR/fixable.rs:129:5
|
LL | / if x && y {
LL | | return true;
@@ -121,7 +121,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x && y`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:136:5
+ --> $DIR/fixable.rs:137:5
|
LL | / if x && y {
LL | | return false;
@@ -131,7 +131,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !(x && y)`
error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:144:8
+ --> $DIR/fixable.rs:145:8
|
LL | if x == true {};
| ^^^^^^^^^ help: try simplifying it as shown: `x`
@@ -139,25 +139,25 @@ LL | if x == true {};
= note: `-D clippy::bool-comparison` implied by `-D warnings`
error: equality checks against false can be replaced by a negation
- --> $DIR/fixable.rs:148:8
+ --> $DIR/fixable.rs:149:8
|
LL | if x == false {};
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:158:8
+ --> $DIR/fixable.rs:159: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:159:8
+ --> $DIR/fixable.rs:160: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:168:12
+ --> $DIR/fixable.rs:169:12
|
LL | } else if returns_bool() {
| ____________^
@@ -168,7 +168,7 @@ LL | | };
| |_____^ help: you can reduce it to: `{ !returns_bool() }`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:181:5
+ --> $DIR/fixable.rs:182:5
|
LL | / if unsafe { no(4) } & 1 != 0 {
LL | | true
@@ -178,13 +178,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:186:30
+ --> $DIR/fixable.rs:187: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:189:9
+ --> $DIR/fixable.rs:190: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_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index 425e6eb62..1dfbee150 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -4,7 +4,8 @@
unused,
clippy::uninlined_format_args,
clippy::unnecessary_mut_passed,
- clippy::unnecessary_to_owned
+ clippy::unnecessary_to_owned,
+ clippy::unnecessary_literal_unwrap
)]
#![warn(clippy::needless_borrow)]
@@ -491,3 +492,15 @@ mod issue_9782_method_variant {
S.foo::<&[u8; 100]>(&a);
}
}
+
+mod issue_10535 {
+ static SOME_STATIC: String = String::new();
+
+ static UNIT: () = compute(&SOME_STATIC);
+
+ pub const fn compute<T>(_: T)
+ where
+ T: Copy,
+ {
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index 3f7fa4a9d..3c0d73f5f 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -4,7 +4,8 @@
unused,
clippy::uninlined_format_args,
clippy::unnecessary_mut_passed,
- clippy::unnecessary_to_owned
+ clippy::unnecessary_to_owned,
+ clippy::unnecessary_literal_unwrap
)]
#![warn(clippy::needless_borrow)]
@@ -491,3 +492,15 @@ mod issue_9782_method_variant {
S.foo::<&[u8; 100]>(&a);
}
}
+
+mod issue_10535 {
+ static SOME_STATIC: String = String::new();
+
+ static UNIT: () = compute(&SOME_STATIC);
+
+ pub const fn compute<T>(_: T)
+ where
+ T: Copy,
+ {
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index d26c31712..f85b4fb46 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -1,5 +1,5 @@
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:15:15
+ --> $DIR/needless_borrow.rs:16:15
|
LL | let _ = x(&&a); // warn
| ^^^ help: change this to: `&a`
@@ -7,211 +7,211 @@ LL | let _ = x(&&a); // warn
= note: `-D clippy::needless-borrow` implied by `-D warnings`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:19:13
+ --> $DIR/needless_borrow.rs:20:13
|
LL | mut_ref(&mut &mut b); // warn
| ^^^^^^^^^^^ help: change this to: `&mut b`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:31:13
+ --> $DIR/needless_borrow.rs:32:13
|
LL | &&a
| ^^^ help: change this to: `&a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:33:15
+ --> $DIR/needless_borrow.rs:34:15
|
LL | 46 => &&a,
| ^^^ help: change this to: `&a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:39:27
+ --> $DIR/needless_borrow.rs:40:27
|
LL | break &ref_a;
| ^^^^^^ help: change this to: `ref_a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:46:15
+ --> $DIR/needless_borrow.rs:47:15
|
LL | let _ = x(&&&a);
| ^^^^ help: change this to: `&a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:47:15
+ --> $DIR/needless_borrow.rs:48:15
|
LL | let _ = x(&mut &&a);
| ^^^^^^^^ help: change this to: `&a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:48:15
+ --> $DIR/needless_borrow.rs:49:15
|
LL | let _ = x(&&&mut b);
| ^^^^^^^^ help: change this to: `&mut b`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:49:15
+ --> $DIR/needless_borrow.rs:50:15
|
LL | let _ = x(&&ref_a);
| ^^^^^^^ help: change this to: `ref_a`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:52:11
+ --> $DIR/needless_borrow.rs:53:11
|
LL | x(&b);
| ^^ help: change this to: `b`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:59:13
+ --> $DIR/needless_borrow.rs:60:13
|
LL | mut_ref(&mut x);
| ^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:60:13
+ --> $DIR/needless_borrow.rs:61:13
|
LL | mut_ref(&mut &mut x);
| ^^^^^^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:61:23
+ --> $DIR/needless_borrow.rs:62:23
|
LL | let y: &mut i32 = &mut x;
| ^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:62:23
+ --> $DIR/needless_borrow.rs:63:23
|
LL | let y: &mut i32 = &mut &mut x;
| ^^^^^^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:71:14
+ --> $DIR/needless_borrow.rs:72:14
|
LL | 0 => &mut x,
| ^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:77:14
+ --> $DIR/needless_borrow.rs:78:14
|
LL | 0 => &mut x,
| ^^^^^^ help: change this to: `x`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:89:13
+ --> $DIR/needless_borrow.rs:90:13
|
LL | let _ = (&x).0;
| ^^^^ help: change this to: `x`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:91:22
+ --> $DIR/needless_borrow.rs:92:22
|
LL | let _ = unsafe { (&*x).0 };
| ^^^^^ help: change this to: `(*x)`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:101:5
+ --> $DIR/needless_borrow.rs:102:5
|
LL | (&&()).foo();
| ^^^^^^ help: change this to: `(&())`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:110:5
+ --> $DIR/needless_borrow.rs:111:5
|
LL | (&&5).foo();
| ^^^^^ help: change this to: `(&5)`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:135:51
+ --> $DIR/needless_borrow.rs:136:51
|
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:136:44
+ --> $DIR/needless_borrow.rs:137:44
|
LL | let _ = std::path::Path::new(".").join(&&".");
| ^^^^^ help: change this to: `"."`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:137:23
+ --> $DIR/needless_borrow.rs:138:23
|
LL | deref_target_is_x(&X);
| ^^ help: change this to: `X`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:138:26
+ --> $DIR/needless_borrow.rs:139:26
|
LL | multiple_constraints(&[[""]]);
| ^^^^^^^ help: change this to: `[[""]]`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:139:45
+ --> $DIR/needless_borrow.rs:140:45
|
LL | multiple_constraints_normalizes_to_same(&X, X);
| ^^ help: change this to: `X`
error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:140:32
+ --> $DIR/needless_borrow.rs:141:32
|
LL | let _ = Some("").unwrap_or(&"");
| ^^^ help: change this to: `""`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:141:33
+ --> $DIR/needless_borrow.rs:142:33
|
LL | let _ = std::fs::write("x", &"".to_string());
| ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:190:13
+ --> $DIR/needless_borrow.rs:191:13
|
LL | (&self.f)()
| ^^^^^^^^^ help: change this to: `(self.f)`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:199:13
+ --> $DIR/needless_borrow.rs:200:13
|
LL | (&mut self.f)()
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:283:20
+ --> $DIR/needless_borrow.rs:284:20
|
LL | takes_iter(&mut x)
| ^^^^^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:297:55
+ --> $DIR/needless_borrow.rs:298:55
|
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:335:37
+ --> $DIR/needless_borrow.rs:336:37
|
LL | let _ = std::fs::write("x", &arg);
| ^^^^ help: change this to: `arg`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:336:37
+ --> $DIR/needless_borrow.rs:337:37
|
LL | let _ = std::fs::write("x", &loc);
| ^^^^ help: change this to: `loc`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:354:15
+ --> $DIR/needless_borrow.rs:355:15
|
LL | debug(&x);
| ^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:363:15
+ --> $DIR/needless_borrow.rs:364:15
|
LL | use_x(&x);
| ^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:457:13
+ --> $DIR/needless_borrow.rs:458:13
|
LL | foo(&a);
| ^^ help: change this to: `a`
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
index 6663520da..59a38425b 100644
--- a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
@@ -5,7 +5,8 @@
unused,
irrefutable_let_patterns,
non_shorthand_field_patterns,
- clippy::needless_borrow
+ clippy::needless_borrow,
+ clippy::needless_if
)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs
index 6c8efd2ce..e48b19cb1 100644
--- a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs
@@ -5,7 +5,8 @@
unused,
irrefutable_let_patterns,
non_shorthand_field_patterns,
- clippy::needless_borrow
+ clippy::needless_borrow,
+ clippy::needless_if
)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
index 8d0f0c258..35497a01e 100644
--- a/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
@@ -1,5 +1,5 @@
error: this pattern takes a reference on something that is being dereferenced
- --> $DIR/needless_borrowed_ref.rs:31:34
+ --> $DIR/needless_borrowed_ref.rs:32:34
|
LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty());
| ^^^^^^
@@ -12,7 +12,7 @@ LL + let _ = v.iter_mut().filter(|a| a.is_empty());
|
error: this pattern takes a reference on something that is being dereferenced
- --> $DIR/needless_borrowed_ref.rs:35:17
+ --> $DIR/needless_borrowed_ref.rs:36:17
|
LL | if let Some(&ref v) = thingy {}
| ^^^^^^
@@ -24,7 +24,7 @@ LL + if let Some(v) = thingy {}
|
error: this pattern takes a reference on something that is being dereferenced
- --> $DIR/needless_borrowed_ref.rs:37:14
+ --> $DIR/needless_borrowed_ref.rs:38:14
|
LL | if let &[&ref a, ref b] = slice_of_refs {}
| ^^^^^^
@@ -36,7 +36,7 @@ LL + if let &[a, ref b] = slice_of_refs {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:39:9
+ --> $DIR/needless_borrowed_ref.rs:40:9
|
LL | let &[ref a, ..] = &array;
| ^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL + let [a, ..] = &array;
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:40:9
+ --> $DIR/needless_borrowed_ref.rs:41:9
|
LL | let &[ref a, ref b, ..] = &array;
| ^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL + let [a, b, ..] = &array;
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:42:12
+ --> $DIR/needless_borrowed_ref.rs:43:12
|
LL | if let &[ref a, ref b] = slice {}
| ^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL + if let [a, b] = slice {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:43:12
+ --> $DIR/needless_borrowed_ref.rs:44:12
|
LL | if let &[ref a, ref b] = &vec[..] {}
| ^^^^^^^^^^^^^^^
@@ -84,7 +84,7 @@ LL + if let [a, b] = &vec[..] {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:45:12
+ --> $DIR/needless_borrowed_ref.rs:46:12
|
LL | if let &[ref a, ref b, ..] = slice {}
| ^^^^^^^^^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL + if let [a, b, ..] = slice {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:46:12
+ --> $DIR/needless_borrowed_ref.rs:47:12
|
LL | if let &[ref a, .., ref b] = slice {}
| ^^^^^^^^^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL + if let [a, .., b] = slice {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:47:12
+ --> $DIR/needless_borrowed_ref.rs:48:12
|
LL | if let &[.., ref a, ref b] = slice {}
| ^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL + if let [.., a, b] = slice {}
|
error: dereferencing a slice pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:49:12
+ --> $DIR/needless_borrowed_ref.rs:50:12
|
LL | if let &[ref a, _] = slice {}
| ^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL + if let [a, _] = slice {}
|
error: dereferencing a tuple pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:51:12
+ --> $DIR/needless_borrowed_ref.rs:52:12
|
LL | if let &(ref a, ref b, ref c) = &tuple {}
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL + if let (a, b, c) = &tuple {}
|
error: dereferencing a tuple pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:52:12
+ --> $DIR/needless_borrowed_ref.rs:53:12
|
LL | if let &(ref a, _, ref c) = &tuple {}
| ^^^^^^^^^^^^^^^^^^
@@ -156,7 +156,7 @@ LL + if let (a, _, c) = &tuple {}
|
error: dereferencing a tuple pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:53:12
+ --> $DIR/needless_borrowed_ref.rs:54:12
|
LL | if let &(ref a, ..) = &tuple {}
| ^^^^^^^^^^^^
@@ -168,7 +168,7 @@ LL + if let (a, ..) = &tuple {}
|
error: dereferencing a tuple pattern where every element takes a reference
- --> $DIR/needless_borrowed_ref.rs:55:12
+ --> $DIR/needless_borrowed_ref.rs:56:12
|
LL | if let &TupleStruct(ref a, ..) = &tuple_struct {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -180,7 +180,7 @@ LL + if let TupleStruct(a, ..) = &tuple_struct {}
|
error: dereferencing a struct pattern where every field's pattern takes a reference
- --> $DIR/needless_borrowed_ref.rs:57:12
+ --> $DIR/needless_borrowed_ref.rs:58:12
|
LL | if let &Struct {
| ____________^
@@ -199,7 +199,7 @@ LL ~ c: renamed,
|
error: dereferencing a struct pattern where every field's pattern takes a reference
- --> $DIR/needless_borrowed_ref.rs:64:12
+ --> $DIR/needless_borrowed_ref.rs:65:12
|
LL | if let &Struct { ref a, b: _, .. } = &s {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed
index b7e80af50..0f0aaad17 100644
--- a/src/tools/clippy/tests/ui/needless_collect.fixed
+++ b/src/tools/clippy/tests/ui/needless_collect.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
+#![allow(unused, clippy::needless_if, clippy::suspicious_map, clippy::iter_count)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
@@ -70,6 +70,11 @@ fn main() {
bar((0..10).collect::<Vec<_>>(), (0..10));
baz((0..10), (), ('a'..='z'))
}
+
+ let values = [1, 2, 3, 4];
+ let mut out = vec![];
+ values.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = values.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
}
fn foo(_: impl IntoIterator<Item = usize>) {}
diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs
index 680b6fa5b..4f48f24b1 100644
--- a/src/tools/clippy/tests/ui/needless_collect.rs
+++ b/src/tools/clippy/tests/ui/needless_collect.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
+#![allow(unused, clippy::needless_if, clippy::suspicious_map, clippy::iter_count)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
@@ -70,6 +70,11 @@ fn main() {
bar((0..10).collect::<Vec<_>>(), (0..10).collect::<Vec<_>>());
baz((0..10), (), ('a'..='z').collect::<Vec<_>>())
}
+
+ let values = [1, 2, 3, 4];
+ let mut out = vec![];
+ values.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = values.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
}
fn foo(_: impl IntoIterator<Item = usize>) {}
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
index fe4209e99..d3d856c2c 100644
--- a/src/tools/clippy/tests/ui/needless_collect_indirect.rs
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
@@ -1,4 +1,5 @@
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
+#![allow(clippy::needless_if, clippy::uninlined_format_args)]
#![warn(clippy::needless_collect)]
use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
index 790d72590..8f84c5596 100644
--- a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
@@ -1,5 +1,5 @@
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:8:39
+ --> $DIR/needless_collect_indirect.rs:9:39
|
LL | let indirect_iter = sample.iter().collect::<Vec<_>>();
| ^^^^^^^
@@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:10:38
+ --> $DIR/needless_collect_indirect.rs:11:38
|
LL | let indirect_len = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
@@ -28,7 +28,7 @@ LL ~ sample.iter().count();
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:12:40
+ --> $DIR/needless_collect_indirect.rs:13:40
|
LL | let indirect_empty = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
@@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none();
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:14:43
+ --> $DIR/needless_collect_indirect.rs:15:43
|
LL | let indirect_contains = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
@@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:26:48
+ --> $DIR/needless_collect_indirect.rs:27:48
|
LL | let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
| ^^^^^^^
@@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:55:51
+ --> $DIR/needless_collect_indirect.rs:56:51
|
LL | let buffer: Vec<&str> = string.split('/').collect();
| ^^^^^^^
@@ -84,7 +84,7 @@ LL ~ string.split('/').count()
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:60:55
+ --> $DIR/needless_collect_indirect.rs:61:55
|
LL | let indirect_len: VecDeque<_> = sample.iter().collect();
| ^^^^^^^
@@ -98,7 +98,7 @@ LL ~ sample.iter().count()
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:65:57
+ --> $DIR/needless_collect_indirect.rs:66:57
|
LL | let indirect_len: LinkedList<_> = sample.iter().collect();
| ^^^^^^^
@@ -112,7 +112,7 @@ LL ~ sample.iter().count()
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:70:57
+ --> $DIR/needless_collect_indirect.rs:71:57
|
LL | let indirect_len: BinaryHeap<_> = sample.iter().collect();
| ^^^^^^^
@@ -126,7 +126,7 @@ LL ~ sample.iter().count()
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:130:59
+ --> $DIR/needless_collect_indirect.rs:131:59
|
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
| ^^^^^^^
@@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:155:59
+ --> $DIR/needless_collect_indirect.rs:156:59
|
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
| ^^^^^^^
@@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:184:63
+ --> $DIR/needless_collect_indirect.rs:185:63
|
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
| ^^^^^^^
@@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:220:59
+ --> $DIR/needless_collect_indirect.rs:221:59
|
LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
| ^^^^^^^
@@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:245:26
+ --> $DIR/needless_collect_indirect.rs:246:26
|
LL | let w = v.iter().collect::<Vec<_>>();
| ^^^^^^^
@@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() {
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:267:30
+ --> $DIR/needless_collect_indirect.rs:268:30
|
LL | let mut w = v.iter().collect::<Vec<_>>();
| ^^^^^^^
@@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() {
|
error: avoid using `collect()` when not needed
- --> $DIR/needless_collect_indirect.rs:289:30
+ --> $DIR/needless_collect_indirect.rs:290:30
|
LL | let mut w = v.iter().collect::<Vec<_>>();
| ^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_else.fixed b/src/tools/clippy/tests/ui/needless_else.fixed
new file mode 100644
index 000000000..06a161627
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_else.fixed
@@ -0,0 +1,57 @@
+//@run-rustfix
+#![allow(unused)]
+#![warn(clippy::needless_else)]
+#![allow(clippy::suspicious_else_formatting)]
+
+macro_rules! mac {
+ ($test:expr) => {
+ if $test {
+ println!("Test successful!");
+ } else {
+ }
+ };
+}
+
+macro_rules! empty_expansion {
+ () => {};
+}
+
+fn main() {
+ let b = std::hint::black_box(true);
+
+ if b {
+ println!("Foobar");
+ }
+
+ if b {
+ println!("Foobar");
+ } else {
+ // Do not lint because this comment might be important
+ }
+
+ if b {
+ println!("Foobar");
+ } else
+ /* Do not lint because this comment might be important */
+ {
+ }
+
+ // Do not lint because of the expression
+ let _ = if b { 1 } else { 2 };
+
+ // Do not lint because inside a macro
+ mac!(b);
+
+ if b {
+ println!("Foobar");
+ } else {
+ #[cfg(foo)]
+ "Do not lint cfg'd out code"
+ }
+
+ if b {
+ println!("Foobar");
+ } else {
+ empty_expansion!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_else.rs b/src/tools/clippy/tests/ui/needless_else.rs
new file mode 100644
index 000000000..728032c47
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_else.rs
@@ -0,0 +1,58 @@
+//@run-rustfix
+#![allow(unused)]
+#![warn(clippy::needless_else)]
+#![allow(clippy::suspicious_else_formatting)]
+
+macro_rules! mac {
+ ($test:expr) => {
+ if $test {
+ println!("Test successful!");
+ } else {
+ }
+ };
+}
+
+macro_rules! empty_expansion {
+ () => {};
+}
+
+fn main() {
+ let b = std::hint::black_box(true);
+
+ if b {
+ println!("Foobar");
+ } else {
+ }
+
+ if b {
+ println!("Foobar");
+ } else {
+ // Do not lint because this comment might be important
+ }
+
+ if b {
+ println!("Foobar");
+ } else
+ /* Do not lint because this comment might be important */
+ {
+ }
+
+ // Do not lint because of the expression
+ let _ = if b { 1 } else { 2 };
+
+ // Do not lint because inside a macro
+ mac!(b);
+
+ if b {
+ println!("Foobar");
+ } else {
+ #[cfg(foo)]
+ "Do not lint cfg'd out code"
+ }
+
+ if b {
+ println!("Foobar");
+ } else {
+ empty_expansion!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_else.stderr b/src/tools/clippy/tests/ui/needless_else.stderr
new file mode 100644
index 000000000..ea6930851
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_else.stderr
@@ -0,0 +1,12 @@
+error: this else branch is empty
+ --> $DIR/needless_else.rs:24:7
+ |
+LL | } else {
+ | _______^
+LL | | }
+ | |_____^ help: you can remove it
+ |
+ = note: `-D clippy::needless-else` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed
new file mode 100644
index 000000000..5e6e140c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_if.fixed
@@ -0,0 +1,93 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(let_chains)]
+#![allow(
+ clippy::blocks_in_if_conditions,
+ clippy::if_same_then_else,
+ clippy::ifs_same_cond,
+ clippy::let_unit_value,
+ clippy::needless_else,
+ clippy::no_effect,
+ clippy::nonminimal_bool,
+ clippy::short_circuit_statement,
+ clippy::unnecessary_operation,
+ unused
+)]
+#![warn(clippy::needless_if)]
+
+extern crate proc_macros;
+use proc_macros::external;
+use proc_macros::with_span;
+
+fn maybe_side_effect() -> bool {
+ true
+}
+
+fn main() {
+ // Lint
+
+ // Do not remove the condition
+ maybe_side_effect();
+ // Do not lint
+ if (true) {
+ } else {
+ }
+ ({
+ return;
+ });
+ // Do not lint if `else if` is present
+ if (true) {
+ } else if (true) {
+ }
+ // Do not lint `if let` or let chains
+ if 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 }
+ } && true);
+ external! { if (true) {} }
+ with_span! {
+ span
+ if (true) {}
+ }
+
+ if true {
+ // comment
+ }
+
+ if true {
+ #[cfg(any())]
+ foo;
+ }
+
+ macro_rules! empty_expansion {
+ () => {};
+ }
+
+ if true {
+ empty_expansion!();
+ }
+
+ macro_rules! empty_repetition {
+ ($($t:tt)*) => {
+ if true {
+ $($t)*
+ }
+ }
+ }
+
+ empty_repetition!();
+
+ // Must be placed into an expression context to not be interpreted as a block
+ ({ maybe_side_effect() });
+ // Would be a block followed by `&&true` - a double reference to `true`
+ ({ maybe_side_effect() } && true);
+
+ // Don't leave trailing attributes
+ #[allow(unused)]
+ true;
+
+ let () = if maybe_side_effect() {};
+}
diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs
new file mode 100644
index 000000000..eb28ce73b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_if.rs
@@ -0,0 +1,94 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(let_chains)]
+#![allow(
+ clippy::blocks_in_if_conditions,
+ clippy::if_same_then_else,
+ clippy::ifs_same_cond,
+ clippy::let_unit_value,
+ clippy::needless_else,
+ clippy::no_effect,
+ clippy::nonminimal_bool,
+ clippy::short_circuit_statement,
+ clippy::unnecessary_operation,
+ unused
+)]
+#![warn(clippy::needless_if)]
+
+extern crate proc_macros;
+use proc_macros::external;
+use proc_macros::with_span;
+
+fn maybe_side_effect() -> bool {
+ true
+}
+
+fn main() {
+ // Lint
+ if (true) {}
+ // Do not remove the condition
+ if maybe_side_effect() {}
+ // Do not lint
+ if (true) {
+ } else {
+ }
+ if {
+ return;
+ } {}
+ // Do not lint if `else if` is present
+ if (true) {
+ } else if (true) {
+ }
+ // Do not lint `if let` or let chains
+ if 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 }
+ } && true
+ {}
+ external! { if (true) {} }
+ with_span! {
+ span
+ if (true) {}
+ }
+
+ if true {
+ // comment
+ }
+
+ if true {
+ #[cfg(any())]
+ foo;
+ }
+
+ macro_rules! empty_expansion {
+ () => {};
+ }
+
+ if true {
+ empty_expansion!();
+ }
+
+ macro_rules! empty_repetition {
+ ($($t:tt)*) => {
+ if true {
+ $($t)*
+ }
+ }
+ }
+
+ empty_repetition!();
+
+ // Must be placed into an expression context to not be interpreted as a block
+ if { maybe_side_effect() } {}
+ // Would be a block followed by `&&true` - a double reference to `true`
+ if { maybe_side_effect() } && true {}
+
+ // Don't leave trailing attributes
+ #[allow(unused)]
+ if true {}
+
+ let () = if maybe_side_effect() {};
+}
diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_if.stderr
new file mode 100644
index 000000000..5cb42c369
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_if.stderr
@@ -0,0 +1,65 @@
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:28:5
+ |
+LL | if (true) {}
+ | ^^^^^^^^^^^^ help: you can remove it
+ |
+ = note: `-D clippy::needless-if` implied by `-D warnings`
+
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:30:5
+ |
+LL | if maybe_side_effect() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();`
+
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:35:5
+ |
+LL | / if {
+LL | | return;
+LL | | } {}
+ | |________^
+ |
+help: you can remove it
+ |
+LL ~ ({
+LL + return;
+LL + });
+ |
+
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:47:5
+ |
+LL | / if {
+LL | | if let true = true && true { true } else { false }
+LL | | } && true
+LL | | {}
+ | |______^
+ |
+help: you can remove it
+ |
+LL ~ ({
+LL + if let true = true && true { true } else { false }
+LL + } && true);
+ |
+
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:85:5
+ |
+LL | if { maybe_side_effect() } {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });`
+
+error: this `if` branch is empty
+ --> $DIR/needless_if.rs:87: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:91:5
+ |
+LL | if true {}
+ | ^^^^^^^^^^ help: you can remove it: `true;`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed
index 92f7b3f77..933dd8bed 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.fixed
+++ b/src/tools/clippy/tests/ui/needless_late_init.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(let_chains)]
#![allow(unused)]
#![allow(
@@ -8,7 +8,8 @@
clippy::let_and_return,
clippy::let_unit_value,
clippy::nonminimal_bool,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
index be378c42f..ba3a04e08 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.rs
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![feature(let_chains)]
#![allow(unused)]
#![allow(
@@ -8,7 +8,8 @@
clippy::let_and_return,
clippy::let_unit_value,
clippy::nonminimal_bool,
- clippy::uninlined_format_args
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
index eff782f8b..78ba8e11c 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.stderr
+++ b/src/tools/clippy/tests/ui/needless_late_init.stderr
@@ -1,5 +1,5 @@
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:27:5
+ --> $DIR/needless_late_init.rs:28:5
|
LL | let a;
| ^^^^^^ created here
@@ -13,7 +13,7 @@ LL | let a = "zero";
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:30:5
+ --> $DIR/needless_late_init.rs:31:5
|
LL | let b;
| ^^^^^^ created here
@@ -27,7 +27,7 @@ LL | let b = 1;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:31:5
+ --> $DIR/needless_late_init.rs:32:5
|
LL | let c;
| ^^^^^^ created here
@@ -41,7 +41,7 @@ LL | let c = 2;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:35:5
+ --> $DIR/needless_late_init.rs:36:5
|
LL | let d: usize;
| ^^^^^^^^^^^^^ created here
@@ -54,7 +54,7 @@ LL | let d: usize = 1;
| ~~~~~~~~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:38:5
+ --> $DIR/needless_late_init.rs:39:5
|
LL | let e;
| ^^^^^^ created here
@@ -67,7 +67,7 @@ LL | let e = format!("{}", d);
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:43:5
+ --> $DIR/needless_late_init.rs:44:5
|
LL | let a;
| ^^^^^^
@@ -88,7 +88,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:52:5
+ --> $DIR/needless_late_init.rs:53:5
|
LL | let b;
| ^^^^^^
@@ -109,7 +109,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:59:5
+ --> $DIR/needless_late_init.rs:60:5
|
LL | let d;
| ^^^^^^
@@ -130,7 +130,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:67:5
+ --> $DIR/needless_late_init.rs:68:5
|
LL | let e;
| ^^^^^^
@@ -151,7 +151,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:74:5
+ --> $DIR/needless_late_init.rs:75:5
|
LL | let f;
| ^^^^^^
@@ -167,7 +167,7 @@ LL + 1 => "three",
|
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:80:5
+ --> $DIR/needless_late_init.rs:81:5
|
LL | let g: usize;
| ^^^^^^^^^^^^^
@@ -187,7 +187,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:88:5
+ --> $DIR/needless_late_init.rs:89:5
|
LL | let x;
| ^^^^^^ created here
@@ -201,7 +201,7 @@ LL | let x = 1;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:92:5
+ --> $DIR/needless_late_init.rs:93:5
|
LL | let x;
| ^^^^^^ created here
@@ -215,7 +215,7 @@ LL | let x = SignificantDrop;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:96:5
+ --> $DIR/needless_late_init.rs:97:5
|
LL | let x;
| ^^^^^^ created here
@@ -229,7 +229,7 @@ LL | let x = SignificantDrop;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:115:5
+ --> $DIR/needless_late_init.rs:116:5
|
LL | let a;
| ^^^^^^
@@ -250,7 +250,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:132:5
+ --> $DIR/needless_late_init.rs:133:5
|
LL | let a;
| ^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
index 7b99042f7..302a3f9ed 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::needless_lifetimes)]
#![allow(
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
index 6fcf1efc2..b15477c92 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::needless_lifetimes)]
#![allow(
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
index 86acc4e00..0da67b600 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
@@ -1,8 +1,8 @@
error: the following explicit lifetimes could be elided: 'a, 'b
- --> $DIR/needless_lifetimes.rs:18:1
+ --> $DIR/needless_lifetimes.rs:18:23
|
LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^ ^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
help: elide the lifetimes
@@ -12,10 +12,10 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
|
error: the following explicit lifetimes could be elided: 'a, 'b
- --> $DIR/needless_lifetimes.rs:20:1
+ --> $DIR/needless_lifetimes.rs:20:24
|
LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -24,10 +24,10 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:30:1
+ --> $DIR/needless_lifetimes.rs:30:15
|
LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -36,10 +36,10 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 {
|
error: the following explicit lifetimes could be elided: 'b
- --> $DIR/needless_lifetimes.rs:42:1
+ --> $DIR/needless_lifetimes.rs:42:31
|
LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -48,10 +48,10 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:49:1
+ --> $DIR/needless_lifetimes.rs:49:27
|
LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -60,10 +60,10 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 {
|
error: the following explicit lifetimes could be elided: 'b
- --> $DIR/needless_lifetimes.rs:66:1
+ --> $DIR/needless_lifetimes.rs:66:26
|
LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -72,10 +72,10 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:73:1
+ --> $DIR/needless_lifetimes.rs:73:22
|
LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -84,10 +84,10 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:82:1
+ --> $DIR/needless_lifetimes.rs:82:21
|
LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -96,10 +96,10 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:87:1
+ --> $DIR/needless_lifetimes.rs:87:28
|
LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -108,10 +108,10 @@ LL + fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()>
|
error: the following explicit lifetimes could be elided: 'a, 'b
- --> $DIR/needless_lifetimes.rs:99:1
+ --> $DIR/needless_lifetimes.rs:99:21
|
LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -120,10 +120,10 @@ LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:123:1
+ --> $DIR/needless_lifetimes.rs:123:15
|
LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -132,10 +132,10 @@ LL + fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I>
|
error: the following explicit lifetimes could be elided: 's
- --> $DIR/needless_lifetimes.rs:153:5
+ --> $DIR/needless_lifetimes.rs:153:21
|
LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -144,10 +144,10 @@ LL + fn self_and_out(&self) -> &u8 {
|
error: the following explicit lifetimes could be elided: 't
- --> $DIR/needless_lifetimes.rs:160:5
+ --> $DIR/needless_lifetimes.rs:160:30
|
LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -156,10 +156,10 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 {
|
error: the following explicit lifetimes could be elided: 's
- --> $DIR/needless_lifetimes.rs:167:5
+ --> $DIR/needless_lifetimes.rs:167:26
|
LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -168,10 +168,10 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 {
|
error: the following explicit lifetimes could be elided: 's, 't
- --> $DIR/needless_lifetimes.rs:171:5
+ --> $DIR/needless_lifetimes.rs:171:29
|
LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -180,10 +180,10 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:190:1
+ --> $DIR/needless_lifetimes.rs:190:19
|
LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -192,10 +192,10 @@ LL + fn struct_with_lt(_foo: Foo<'_>) -> &str {
|
error: the following explicit lifetimes could be elided: 'b
- --> $DIR/needless_lifetimes.rs:208:1
+ --> $DIR/needless_lifetimes.rs:208:25
|
LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -204,10 +204,10 @@ LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:216:1
+ --> $DIR/needless_lifetimes.rs:216:21
|
LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -216,10 +216,10 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:231:1
+ --> $DIR/needless_lifetimes.rs:231:22
|
LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -228,10 +228,10 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:237:1
+ --> $DIR/needless_lifetimes.rs:237:18
|
LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -240,10 +240,10 @@ LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str {
|
error: the following explicit lifetimes could be elided: 'b
- --> $DIR/needless_lifetimes.rs:255:1
+ --> $DIR/needless_lifetimes.rs:255:24
|
LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -252,10 +252,10 @@ LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:263:1
+ --> $DIR/needless_lifetimes.rs:263:20
|
LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -264,10 +264,10 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:267:1
+ --> $DIR/needless_lifetimes.rs:267:30
|
LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^
|
help: elide the lifetimes
|
@@ -276,10 +276,10 @@ LL + fn named_input_elided_output(_arg: &str) -> &str {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:275:1
+ --> $DIR/needless_lifetimes.rs:275:19
|
LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -288,10 +288,10 @@ LL + fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:311:1
+ --> $DIR/needless_lifetimes.rs:311:24
|
LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -300,10 +300,10 @@ LL + fn out_return_type_lts(e: &str) -> Cow<'_> {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:318:9
+ --> $DIR/needless_lifetimes.rs:318:24
|
LL | fn needless_lt<'a>(x: &'a u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -312,10 +312,10 @@ LL + fn needless_lt(x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:322:9
+ --> $DIR/needless_lifetimes.rs:322:24
|
LL | fn needless_lt<'a>(_x: &'a u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -324,10 +324,10 @@ LL + fn needless_lt(_x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:335:9
+ --> $DIR/needless_lifetimes.rs:335:16
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -336,10 +336,10 @@ LL + fn baz(&self) -> impl Foo + '_ {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:367:5
+ --> $DIR/needless_lifetimes.rs:367:55
|
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -348,10 +348,10 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:376:5
+ --> $DIR/needless_lifetimes.rs:376:26
|
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -360,10 +360,10 @@ LL + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:388:5
+ --> $DIR/needless_lifetimes.rs:388:32
|
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -372,10 +372,10 @@ LL + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:403:5
+ --> $DIR/needless_lifetimes.rs:403:28
|
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -384,10 +384,10 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:416:5
+ --> $DIR/needless_lifetimes.rs:416:28
|
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -396,10 +396,10 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:419:5
+ --> $DIR/needless_lifetimes.rs:419:28
|
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -408,10 +408,10 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:441:9
+ --> $DIR/needless_lifetimes.rs:441:21
|
LL | fn implicit<'a>(&'a self) -> &'a () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -420,10 +420,10 @@ LL + fn implicit(&self) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:444:9
+ --> $DIR/needless_lifetimes.rs:444:25
|
LL | fn implicit_mut<'a>(&'a mut self) -> &'a () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -432,10 +432,10 @@ LL + fn implicit_mut(&mut self) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:455:9
+ --> $DIR/needless_lifetimes.rs:455:31
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -444,10 +444,10 @@ LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:461:9
+ --> $DIR/needless_lifetimes.rs:461:21
|
LL | fn implicit<'a>(&'a self) -> &'a ();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -456,10 +456,10 @@ LL + fn implicit(&self) -> &();
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:462:9
+ --> $DIR/needless_lifetimes.rs:462:30
|
LL | fn implicit_provided<'a>(&'a self) -> &'a () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -468,10 +468,10 @@ LL + fn implicit_provided(&self) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:471:9
+ --> $DIR/needless_lifetimes.rs:471:31
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -480,10 +480,10 @@ LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &();
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:472:9
+ --> $DIR/needless_lifetimes.rs:472:40
|
LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -492,10 +492,10 @@ LL + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:481:5
+ --> $DIR/needless_lifetimes.rs:481:12
|
LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -504,10 +504,10 @@ LL + fn foo(x: &u8, y: &'_ u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:483:5
+ --> $DIR/needless_lifetimes.rs:483:12
|
LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -516,10 +516,10 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {}
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:490:5
+ --> $DIR/needless_lifetimes.rs:490:18
|
LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
help: elide the lifetimes
|
@@ -528,10 +528,10 @@ LL + fn one_input(x: &u8) -> &u8 {
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:495:5
+ --> $DIR/needless_lifetimes.rs:495:42
|
LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^
|
help: elide the lifetimes
|
@@ -540,10 +540,10 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8)
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:511:9
+ --> $DIR/needless_lifetimes.rs:511:22
|
LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^ ^^ ^^
|
= note: this error originates in the macro `__inline_mac_mod_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
help: elide the lifetimes
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
index 70015fccf..ec981ad97 100644
--- a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
@@ -2,6 +2,7 @@
#![allow(unused)]
#![warn(clippy::needless_option_as_deref)]
+#![allow(clippy::useless_vec)]
fn main() {
// should lint
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
index e2e35360c..6360874f6 100644
--- a/src/tools/clippy/tests/ui/needless_option_as_deref.rs
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
@@ -2,6 +2,7 @@
#![allow(unused)]
#![warn(clippy::needless_option_as_deref)]
+#![allow(clippy::useless_vec)]
fn main() {
// should lint
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
index bc07db5b3..20d28a968 100644
--- a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
@@ -1,5 +1,5 @@
error: derefed type is same as origin
- --> $DIR/needless_option_as_deref.rs:8:29
+ --> $DIR/needless_option_as_deref.rs:9:29
|
LL | let _: Option<&usize> = Some(&1).as_deref();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
@@ -7,13 +7,13 @@ LL | let _: Option<&usize> = Some(&1).as_deref();
= note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
error: derefed type is same as origin
- --> $DIR/needless_option_as_deref.rs:9:33
+ --> $DIR/needless_option_as_deref.rs:10:33
|
LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
error: derefed type is same as origin
- --> $DIR/needless_option_as_deref.rs:13:13
+ --> $DIR/needless_option_as_deref.rs:14:13
|
LL | let _ = x.as_deref_mut();
| ^^^^^^^^^^^^^^^^ help: try this: `x`
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
index 78a0e92d1..c603163c1 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
@@ -1,4 +1,3 @@
-#![crate_type = "proc-macro"]
#![warn(clippy::needless_pass_by_value)]
extern crate proc_macro;
diff --git a/src/tools/clippy/tests/ui/needless_pub_self.fixed b/src/tools/clippy/tests/ui/needless_pub_self.fixed
new file mode 100644
index 000000000..672b4c318
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pub_self.fixed
@@ -0,0 +1,33 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(unused)]
+#![warn(clippy::needless_pub_self)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+ fn a() {}
+ fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(in super) fn d() {}
+ pub(super) fn e() {}
+ fn f() {}
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/needless_pub_self.rs b/src/tools/clippy/tests/ui/needless_pub_self.rs
new file mode 100644
index 000000000..5ac1edf8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pub_self.rs
@@ -0,0 +1,33 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(unused)]
+#![warn(clippy::needless_pub_self)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+pub(self) fn a() {}
+pub(in self) fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(in super) fn d() {}
+ pub(super) fn e() {}
+ pub(self) fn f() {}
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/needless_pub_self.stderr b/src/tools/clippy/tests/ui/needless_pub_self.stderr
new file mode 100644
index 000000000..3aa2feb5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pub_self.stderr
@@ -0,0 +1,22 @@
+error: unnecessary `pub(self)`
+ --> $DIR/needless_pub_self.rs:13:1
+ |
+LL | pub(self) fn a() {}
+ | ^^^^^^^^^ help: remove it
+ |
+ = note: `-D clippy::needless-pub-self` implied by `-D warnings`
+
+error: unnecessary `pub(in self)`
+ --> $DIR/needless_pub_self.rs:14:1
+ |
+LL | pub(in self) fn b() {}
+ | ^^^^^^^^^^^^ help: remove it
+
+error: unnecessary `pub(self)`
+ --> $DIR/needless_pub_self.rs:20:5
+ |
+LL | pub(self) fn f() {}
+ | ^^^^^^^^^ help: remove it
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs
index 921801138..a16ef5a5b 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop.rs
+++ b/src/tools/clippy/tests/ui/needless_range_loop.rs
@@ -1,5 +1,9 @@
#![warn(clippy::needless_range_loop)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap,
+ clippy::useless_vec
+)]
static STATIC: [usize; 4] = [0, 1, 8, 16];
const CONST: [usize; 4] = [0, 1, 8, 16];
@@ -82,6 +86,29 @@ fn main() {
for i in 0..2 {
println!("{}", test[i]);
}
+
+ // See #601
+ for i in 0..10 {
+ // no error, id_col does not exist outside the loop
+ let mut id_col = [0f64; 10];
+ id_col[i] = 1f64;
+ }
+
+ fn f<T>(_: &T, _: &T) -> bool {
+ unimplemented!()
+ }
+ fn g<T>(_: &mut [T], _: usize, _: usize) {
+ unimplemented!()
+ }
+ for i in 1..vec.len() {
+ if f(&vec[i - 1], &vec[i]) {
+ g(&mut vec, i - 1, i);
+ }
+ }
+
+ for mid in 1..vec.len() {
+ let (_, _) = vec.split_at(mid);
+ }
}
struct Test {
@@ -94,3 +121,38 @@ impl std::ops::Index<usize> for Test {
&self.inner[index]
}
}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+ let pivot = v.len() - 1;
+ let mut i = 0;
+ for j in 0..pivot {
+ if v[j] <= v[pivot] {
+ v.swap(i, j);
+ i += 1;
+ }
+ }
+ v.swap(i, pivot);
+ i
+}
+
+pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
+ // Same source and destination - don't trigger lint
+ for i in 0..dst.len() {
+ dst[d + i] = dst[s + i];
+ }
+}
+
+mod issue_2496 {
+ pub trait Handle {
+ fn new_for_index(index: usize) -> Self;
+ fn index(&self) -> usize;
+ }
+
+ pub fn test<H: Handle>() -> H {
+ for x in 0..5 {
+ let next_handle = H::new_for_index(x);
+ println!("{}", next_handle.index());
+ }
+ unimplemented!()
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr
index cffa19bec..8ca6b880c 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop.stderr
+++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr
@@ -1,5 +1,5 @@
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:11:14
+ --> $DIR/needless_range_loop.rs:15:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | for <item> in &vec {
| ~~~~~~ ~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:20:14
+ --> $DIR/needless_range_loop.rs:24:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | for <item> in &vec {
| ~~~~~~ ~~~~
error: the loop variable `j` is only used to index `STATIC`
- --> $DIR/needless_range_loop.rs:25:14
+ --> $DIR/needless_range_loop.rs:29:14
|
LL | for j in 0..4 {
| ^^^^
@@ -33,7 +33,7 @@ LL | for <item> in &STATIC {
| ~~~~~~ ~~~~~~~
error: the loop variable `j` is only used to index `CONST`
- --> $DIR/needless_range_loop.rs:29:14
+ --> $DIR/needless_range_loop.rs:33:14
|
LL | for j in 0..4 {
| ^^^^
@@ -44,7 +44,7 @@ LL | for <item> in &CONST {
| ~~~~~~ ~~~~~~
error: the loop variable `i` is used to index `vec`
- --> $DIR/needless_range_loop.rs:33:14
+ --> $DIR/needless_range_loop.rs:37:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@@ -55,7 +55,7 @@ LL | for (i, <item>) in vec.iter().enumerate() {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec2`
- --> $DIR/needless_range_loop.rs:41:14
+ --> $DIR/needless_range_loop.rs:45:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL | for <item> in vec2.iter().take(vec.len()) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:45:14
+ --> $DIR/needless_range_loop.rs:49:14
|
LL | for i in 5..vec.len() {
| ^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL | for <item> in vec.iter().skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:49:14
+ --> $DIR/needless_range_loop.rs:53:14
|
LL | for i in 0..MAX_LEN {
| ^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | for <item> in vec.iter().take(MAX_LEN) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:53:14
+ --> $DIR/needless_range_loop.rs:57:14
|
LL | for i in 0..=MAX_LEN {
| ^^^^^^^^^^^
@@ -99,7 +99,7 @@ LL | for <item> in vec.iter().take(MAX_LEN + 1) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:57:14
+ --> $DIR/needless_range_loop.rs:61:14
|
LL | for i in 5..10 {
| ^^^^^
@@ -110,7 +110,7 @@ LL | for <item> in vec.iter().take(10).skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop.rs:61:14
+ --> $DIR/needless_range_loop.rs:65:14
|
LL | for i in 5..=10 {
| ^^^^^^
@@ -121,7 +121,7 @@ LL | for <item> in vec.iter().take(10 + 1).skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
- --> $DIR/needless_range_loop.rs:65:14
+ --> $DIR/needless_range_loop.rs:69:14
|
LL | for i in 5..vec.len() {
| ^^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL | for (i, <item>) in vec.iter().enumerate().skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
- --> $DIR/needless_range_loop.rs:69:14
+ --> $DIR/needless_range_loop.rs:73:14
|
LL | for i in 5..10 {
| ^^^^^
@@ -143,7 +143,7 @@ LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
- --> $DIR/needless_range_loop.rs:74:14
+ --> $DIR/needless_range_loop.rs:78:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.rs b/src/tools/clippy/tests/ui/needless_range_loop2.rs
index 7633316e0..516d99a35 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop2.rs
+++ b/src/tools/clippy/tests/ui/needless_range_loop2.rs
@@ -1,4 +1,5 @@
#![warn(clippy::needless_range_loop)]
+#![allow(clippy::useless_vec)]
fn calc_idx(i: usize) -> usize {
(i + i + 20) % 4
diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
index 1e6ec5e66..8c4f5d954 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop2.stderr
+++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
@@ -1,5 +1,5 @@
error: the loop variable `i` is only used to index `ns`
- --> $DIR/needless_range_loop2.rs:10:14
+ --> $DIR/needless_range_loop2.rs:11:14
|
LL | for i in 3..10 {
| ^^^^^
@@ -11,7 +11,7 @@ LL | for <item> in ns.iter().take(10).skip(3) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `ms`
- --> $DIR/needless_range_loop2.rs:31:14
+ --> $DIR/needless_range_loop2.rs:32:14
|
LL | for i in 0..ms.len() {
| ^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | for <item> in &mut ms {
| ~~~~~~ ~~~~~~~
error: the loop variable `i` is only used to index `ms`
- --> $DIR/needless_range_loop2.rs:37:14
+ --> $DIR/needless_range_loop2.rs:38:14
|
LL | for i in 0..ms.len() {
| ^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL | for <item> in &mut ms {
| ~~~~~~ ~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop2.rs:61:14
+ --> $DIR/needless_range_loop2.rs:62:14
|
LL | for i in x..x + 4 {
| ^^^^^^^^
@@ -44,7 +44,7 @@ LL | for <item> in vec.iter_mut().skip(x).take(4) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
- --> $DIR/needless_range_loop2.rs:68:14
+ --> $DIR/needless_range_loop2.rs:69:14
|
LL | for i in x..=x + 4 {
| ^^^^^^^^^
@@ -55,7 +55,7 @@ LL | for <item> in vec.iter_mut().skip(x).take(4 + 1) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `arr`
- --> $DIR/needless_range_loop2.rs:74:14
+ --> $DIR/needless_range_loop2.rs:75:14
|
LL | for i in 0..3 {
| ^^^^
@@ -66,7 +66,7 @@ LL | for <item> in &arr {
| ~~~~~~ ~~~~
error: the loop variable `i` is only used to index `arr`
- --> $DIR/needless_range_loop2.rs:78:14
+ --> $DIR/needless_range_loop2.rs:79:14
|
LL | for i in 0..2 {
| ^^^^
@@ -77,7 +77,7 @@ LL | for <item> in arr.iter().take(2) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `arr`
- --> $DIR/needless_range_loop2.rs:82:14
+ --> $DIR/needless_range_loop2.rs:83:14
|
LL | for i in 1..3 {
| ^^^^
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed
new file mode 100644
index 000000000..b36912efb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed
@@ -0,0 +1,17 @@
+//@run-rustfix
+#![allow(clippy::needless_raw_string_hashes, clippy::no_effect, unused)]
+#![warn(clippy::needless_raw_strings)]
+#![feature(c_str_literals)]
+
+fn main() {
+ "aaa";
+ r#""aaa""#;
+ r#"\s"#;
+ b"aaa";
+ br#""aaa""#;
+ br#"\s"#;
+ // currently disabled: https://github.com/rust-lang/rust/issues/113333
+ // cr#"aaa"#;
+ // cr#""aaa""#;
+ // cr#"\s"#;
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs
new file mode 100644
index 000000000..8f48e7dab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string.rs
@@ -0,0 +1,17 @@
+//@run-rustfix
+#![allow(clippy::needless_raw_string_hashes, clippy::no_effect, unused)]
+#![warn(clippy::needless_raw_strings)]
+#![feature(c_str_literals)]
+
+fn main() {
+ r#"aaa"#;
+ r#""aaa""#;
+ r#"\s"#;
+ br#"aaa"#;
+ br#""aaa""#;
+ br#"\s"#;
+ // currently disabled: https://github.com/rust-lang/rust/issues/113333
+ // cr#"aaa"#;
+ // cr#""aaa""#;
+ // cr#"\s"#;
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr
new file mode 100644
index 000000000..cfb07b647
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr
@@ -0,0 +1,16 @@
+error: unnecessary raw string literal
+ --> $DIR/needless_raw_string.rs:7:5
+ |
+LL | r#"aaa"#;
+ | ^^^^^^^^ help: try: `"aaa"`
+ |
+ = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
+
+error: unnecessary raw string literal
+ --> $DIR/needless_raw_string.rs:10:5
+ |
+LL | br#"aaa"#;
+ | ^^^^^^^^^ help: try: `b"aaa"`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
new file mode 100644
index 000000000..c8507c727
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
@@ -0,0 +1,20 @@
+//@run-rustfix
+#![allow(clippy::no_effect, unused)]
+#![warn(clippy::needless_raw_string_hashes)]
+#![feature(c_str_literals)]
+
+fn main() {
+ r#"aaa"#;
+ r#"Hello "world"!"#;
+ r####" "### "## "# "####;
+ r###" "aa" "# "## "###;
+ br#"aaa"#;
+ br#"Hello "world"!"#;
+ br####" "### "## "# "####;
+ br###" "aa" "# "## "###;
+ // currently disabled: https://github.com/rust-lang/rust/issues/113333
+ // cr#"aaa"#;
+ // cr##"Hello "world"!"##;
+ // cr######" "### "## "# "######;
+ // cr######" "aa" "# "## "######;
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
new file mode 100644
index 000000000..912fbde16
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
@@ -0,0 +1,20 @@
+//@run-rustfix
+#![allow(clippy::no_effect, unused)]
+#![warn(clippy::needless_raw_string_hashes)]
+#![feature(c_str_literals)]
+
+fn main() {
+ r#"aaa"#;
+ r##"Hello "world"!"##;
+ r######" "### "## "# "######;
+ r######" "aa" "# "## "######;
+ br#"aaa"#;
+ br##"Hello "world"!"##;
+ br######" "### "## "# "######;
+ br######" "aa" "# "## "######;
+ // currently disabled: https://github.com/rust-lang/rust/issues/113333
+ // cr#"aaa"#;
+ // cr##"Hello "world"!"##;
+ // cr######" "### "## "# "######;
+ // cr######" "aa" "# "## "######;
+}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
new file mode 100644
index 000000000..30e6783a3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
@@ -0,0 +1,40 @@
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:8:5
+ |
+LL | r##"Hello "world"!"##;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#`
+ |
+ = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
+
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:9:5
+ |
+LL | r######" "### "## "# "######;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####`
+
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:10:5
+ |
+LL | r######" "aa" "# "## "######;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###`
+
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:12:5
+ |
+LL | br##"Hello "world"!"##;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#`
+
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:13:5
+ |
+LL | br######" "### "## "# "######;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####`
+
+error: unnecessary hashes around raw string literal
+ --> $DIR/needless_raw_string_hashes.rs:14:5
+ |
+LL | br######" "aa" "# "## "######;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index d49ae5d86..4dabf3139 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -7,7 +7,8 @@
clippy::if_same_then_else,
clippy::single_match,
clippy::needless_bool,
- clippy::equatable_if_let
+ clippy::equatable_if_let,
+ clippy::needless_else
)]
#![warn(clippy::needless_return)]
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 367638261..542f562b3 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -7,7 +7,8 @@
clippy::if_same_then_else,
clippy::single_match,
clippy::needless_bool,
- clippy::equatable_if_let
+ clippy::equatable_if_let,
+ clippy::needless_else
)]
#![warn(clippy::needless_return)]
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 05f6038cd..1d9d23d30 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -1,390 +1,582 @@
error: unneeded `return` statement
- --> $DIR/needless_return.rs:27:5
+ --> $DIR/needless_return.rs:28:5
|
LL | return true;
| ^^^^^^^^^^^
|
= note: `-D clippy::needless-return` implied by `-D warnings`
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:31:5
+ --> $DIR/needless_return.rs:32:5
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:36:5
+ --> $DIR/needless_return.rs:37:5
|
LL | return true;;;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;;;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:41:5
+ --> $DIR/needless_return.rs:42:5
|
LL | return true;; ; ;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;; ; ;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:46:9
+ --> $DIR/needless_return.rs:47:9
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:48:9
+ --> $DIR/needless_return.rs:49:9
|
LL | return false;
| ^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return false;
+LL + false
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:54:17
+ --> $DIR/needless_return.rs:55:17
|
LL | true => return false,
| ^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL | true => false,
+ | ~~~~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:56:13
+ --> $DIR/needless_return.rs:57:13
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:63:9
+ --> $DIR/needless_return.rs:64:9
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:65:16
+ --> $DIR/needless_return.rs:66:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL | let _ = || true;
+ | ~~~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:69:5
+ --> $DIR/needless_return.rs:70:5
|
LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return the_answer!();
+LL + the_answer!()
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:72:21
+ --> $DIR/needless_return.rs:73:21
|
LL | fn test_void_fun() {
| _____________________^
LL | | return;
| |__________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - fn test_void_fun() {
+LL - return;
+LL + fn test_void_fun() {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:77:11
+ --> $DIR/needless_return.rs:78:11
|
LL | if b {
| ___________^
LL | | return;
| |______________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - if b {
+LL - return;
+LL + if b {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:79:13
+ --> $DIR/needless_return.rs:80:13
|
LL | } else {
| _____________^
LL | | return;
| |______________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - } else {
+LL - return;
+LL + } else {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:87:14
+ --> $DIR/needless_return.rs:88:14
|
LL | _ => return,
| ^^^^^^
|
- = help: replace `return` with a unit value
+help: replace `return` with a unit value
+ |
+LL | _ => (),
+ | ~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:95:24
+ --> $DIR/needless_return.rs:96:24
|
LL | let _ = 42;
| ________________________^
LL | | return;
| |__________________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - let _ = 42;
+LL - return;
+LL + let _ = 42;
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:98:14
+ --> $DIR/needless_return.rs:99:14
|
LL | _ => return,
| ^^^^^^
|
- = help: replace `return` with a unit value
+help: replace `return` with a unit value
+ |
+LL | _ => (),
+ | ~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:111:9
+ --> $DIR/needless_return.rs:112:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return String::from("test");
+LL + String::from("test")
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:113:9
+ --> $DIR/needless_return.rs:114:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return String::new();
+LL + String::new()
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:135:32
+ --> $DIR/needless_return.rs:136:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^
|
- = help: replace `return` with an empty block
+help: replace `return` with an empty block
+ |
+LL | bar.unwrap_or_else(|_| {})
+ | ~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:139:21
+ --> $DIR/needless_return.rs:140:21
|
LL | let _ = || {
| _____________________^
LL | | return;
| |__________________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - let _ = || {
+LL - return;
+LL + let _ = || {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:142:20
+ --> $DIR/needless_return.rs:143:20
|
LL | let _ = || return;
| ^^^^^^
|
- = help: replace `return` with an empty block
+help: replace `return` with an empty block
+ |
+LL | let _ = || {};
+ | ~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:148:32
+ --> $DIR/needless_return.rs:149:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL | res.unwrap_or_else(|_| Foo)
+ | ~~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:157:5
+ --> $DIR/needless_return.rs:158:5
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:161:5
+ --> $DIR/needless_return.rs:162:5
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:166:9
+ --> $DIR/needless_return.rs:167:9
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:168:9
+ --> $DIR/needless_return.rs:169:9
|
LL | return false;
| ^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return false;
+LL + false
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:174:17
+ --> $DIR/needless_return.rs:175:17
|
LL | true => return false,
| ^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL | true => false,
+ | ~~~~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:176:13
+ --> $DIR/needless_return.rs:177:13
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:183:9
+ --> $DIR/needless_return.rs:184:9
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return true;
+LL + true
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:185:16
+ --> $DIR/needless_return.rs:186:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL | let _ = || true;
+ | ~~~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:189:5
+ --> $DIR/needless_return.rs:190:5
|
LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return the_answer!();
+LL + the_answer!()
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:192:33
+ --> $DIR/needless_return.rs:193:33
|
LL | async fn async_test_void_fun() {
| _________________________________^
LL | | return;
| |__________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - async fn async_test_void_fun() {
+LL - return;
+LL + async fn async_test_void_fun() {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:197:11
+ --> $DIR/needless_return.rs:198:11
|
LL | if b {
| ___________^
LL | | return;
| |______________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - if b {
+LL - return;
+LL + if b {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:199:13
+ --> $DIR/needless_return.rs:200:13
|
LL | } else {
| _____________^
LL | | return;
| |______________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - } else {
+LL - return;
+LL + } else {
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:207:14
+ --> $DIR/needless_return.rs:208:14
|
LL | _ => return,
| ^^^^^^
|
- = help: replace `return` with a unit value
+help: replace `return` with a unit value
+ |
+LL | _ => (),
+ | ~~
error: unneeded `return` statement
- --> $DIR/needless_return.rs:220:9
+ --> $DIR/needless_return.rs:221:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return String::from("test");
+LL + String::from("test")
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:222:9
+ --> $DIR/needless_return.rs:223:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return String::new();
+LL + String::new()
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:238:5
+ --> $DIR/needless_return.rs:239:5
|
LL | return format!("Hello {}", "world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return format!("Hello {}", "world!");
+LL + format!("Hello {}", "world!")
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:250:9
+ --> $DIR/needless_return.rs:251:9
|
LL | return true;
| ^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ true
+LL | } else {
+LL | return false;
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:252:9
+ --> $DIR/needless_return.rs:253:9
|
LL | return false;
| ^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ false
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:259:13
+ --> $DIR/needless_return.rs:260:13
|
LL | return 10;
| ^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ 10
+LL | },
+ ...
+LL | },
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:262:13
+ --> $DIR/needless_return.rs:263:13
|
LL | return 100;
| ^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ 100
+LL | },
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:270:9
+ --> $DIR/needless_return.rs:271:9
|
LL | return 0;
| ^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ 0
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:277:13
+ --> $DIR/needless_return.rs:278:13
|
LL | return *(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ *(x as *const isize)
+LL | } else {
+LL | return !*(x as *const isize);
+LL ~ }
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:279:13
+ --> $DIR/needless_return.rs:280:13
|
LL | return !*(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL ~ !*(x as *const isize)
+LL ~ }
+LL ~ }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:286:20
+ --> $DIR/needless_return.rs:287:20
|
LL | let _ = 42;
| ____________________^
@@ -392,47 +584,73 @@ LL | |
LL | | return;
| |______________^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - let _ = 42;
+LL -
+LL - return;
+LL + let _ = 42;
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:293:20
+ --> $DIR/needless_return.rs:294:20
|
LL | let _ = 42; return;
| ^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - let _ = 42; return;
+LL + let _ = 42;
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:305:9
+ --> $DIR/needless_return.rs:306:9
|
LL | return Ok(format!("ok!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return Ok(format!("ok!"));
+LL + Ok(format!("ok!"))
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:307:9
+ --> $DIR/needless_return.rs:308:9
|
LL | return Err(format!("err!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return Err(format!("err!"));
+LL + Err(format!("err!"))
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:313:9
+ --> $DIR/needless_return.rs:314:9
|
LL | return if true { 1 } else { 2 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return`
+help: remove `return`
+ |
+LL - return if true { 1 } else { 2 };
+LL + if true { 1 } else { 2 }
+ |
error: unneeded `return` statement
- --> $DIR/needless_return.rs:317:9
+ --> $DIR/needless_return.rs:318:9
|
LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: remove `return` and wrap the sequence with parentheses
+help: remove `return` and wrap the sequence with parentheses
+ |
+LL - return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
+LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 })
+ |
error: aborting due to 52 previous errors
diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs
index 29821ff96..eb179f30e 100644
--- a/src/tools/clippy/tests/ui/never_loop.rs
+++ b/src/tools/clippy/tests/ui/never_loop.rs
@@ -1,4 +1,6 @@
+#![feature(inline_const)]
#![allow(
+ clippy::eq_op,
clippy::single_match,
unused_assignments,
unused_variables,
@@ -295,6 +297,42 @@ pub fn test24() {
}
}
+// Do not lint, we can evaluate `true` to always succeed thus can short-circuit before the `return`
+pub fn test25() {
+ loop {
+ 'label: {
+ if const { true } {
+ break 'label;
+ }
+ return;
+ }
+ }
+}
+
+pub fn test26() {
+ loop {
+ 'label: {
+ if 1 == 1 {
+ break 'label;
+ }
+ return;
+ }
+ }
+}
+
+pub fn test27() {
+ loop {
+ 'label: {
+ let x = true;
+ // Lints because we cannot prove it's always `true`
+ if x {
+ break 'label;
+ }
+ return;
+ }
+ }
+}
+
fn main() {
test1();
test2();
diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr
index 704d44864..0446c09cd 100644
--- a/src/tools/clippy/tests/ui/never_loop.stderr
+++ b/src/tools/clippy/tests/ui/never_loop.stderr
@@ -1,5 +1,5 @@
error: this loop never actually loops
- --> $DIR/never_loop.rs:10:5
+ --> $DIR/never_loop.rs:12:5
|
LL | / loop {
LL | | // clippy::never_loop
@@ -13,7 +13,7 @@ LL | | }
= note: `#[deny(clippy::never_loop)]` on by default
error: this loop never actually loops
- --> $DIR/never_loop.rs:32:5
+ --> $DIR/never_loop.rs:34:5
|
LL | / loop {
LL | | // never loops
@@ -23,7 +23,7 @@ LL | | }
| |_____^
error: this loop never actually loops
- --> $DIR/never_loop.rs:52:5
+ --> $DIR/never_loop.rs:54:5
|
LL | / loop {
LL | | // never loops
@@ -35,7 +35,7 @@ LL | | }
| |_____^
error: this loop never actually loops
- --> $DIR/never_loop.rs:54:9
+ --> $DIR/never_loop.rs:56:9
|
LL | / while i == 0 {
LL | | // never loops
@@ -44,7 +44,7 @@ LL | | }
| |_________^
error: this loop never actually loops
- --> $DIR/never_loop.rs:66:9
+ --> $DIR/never_loop.rs:68:9
|
LL | / loop {
LL | | // never loops
@@ -56,7 +56,7 @@ LL | | }
| |_________^
error: this loop never actually loops
- --> $DIR/never_loop.rs:102:5
+ --> $DIR/never_loop.rs:104:5
|
LL | / while let Some(y) = x {
LL | | // never loops
@@ -65,7 +65,7 @@ LL | | }
| |_____^
error: this loop never actually loops
- --> $DIR/never_loop.rs:109:5
+ --> $DIR/never_loop.rs:111:5
|
LL | / for x in 0..10 {
LL | | // never loops
@@ -82,7 +82,7 @@ LL | if let Some(x) = (0..10).next() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: this loop never actually loops
- --> $DIR/never_loop.rs:157:5
+ --> $DIR/never_loop.rs:159:5
|
LL | / 'outer: while a {
LL | | // never loops
@@ -94,7 +94,7 @@ LL | | }
| |_____^
error: this loop never actually loops
- --> $DIR/never_loop.rs:172:9
+ --> $DIR/never_loop.rs:174:9
|
LL | / while false {
LL | | break 'label;
@@ -102,7 +102,7 @@ LL | | }
| |_________^
error: this loop never actually loops
- --> $DIR/never_loop.rs:223:13
+ --> $DIR/never_loop.rs:225:13
|
LL | let _ = loop {
| _____________^
@@ -115,7 +115,7 @@ LL | | };
| |_____^
error: this loop never actually loops
- --> $DIR/never_loop.rs:244:5
+ --> $DIR/never_loop.rs:246:5
|
LL | / 'a: loop {
LL | | 'b: {
@@ -126,8 +126,16 @@ LL | | }
LL | | }
| |_____^
+error: sub-expression diverges
+ --> $DIR/never_loop.rs:249:17
+ |
+LL | break 'a;
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
+
error: this loop never actually loops
- --> $DIR/never_loop.rs:278:13
+ --> $DIR/never_loop.rs:280:13
|
LL | / for _ in 0..20 {
LL | | break 'block;
@@ -139,5 +147,17 @@ help: if you need the first element of the iterator, try writing
LL | if let Some(_) = (0..20).next() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-error: aborting due to 12 previous errors
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:324:5
+ |
+LL | / loop {
+LL | | 'label: {
+LL | | let x = true;
+LL | | // Lints because we cannot prove it's always `true`
+... |
+LL | | }
+LL | | }
+ | |_____^
+
+error: aborting due to 14 previous errors
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index a2a30c8b9..4eff62b85 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -401,25 +401,3 @@ mod issue7344 {
}
}
}
-
-mod issue10041 {
- struct Bomb;
-
- impl Bomb {
- // Hidden <Rhs = Self> default generic parameter.
- pub fn new() -> impl PartialOrd {
- 0i32
- }
- }
-
- // TAIT with self-referencing bounds
- type X = impl std::ops::Add<Output = X>;
-
- struct Bomb2;
-
- impl Bomb2 {
- pub fn new() -> X {
- 0i32
- }
- }
-}
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
index 2eaebfb5c..2b053b462 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.stderr
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
@@ -92,21 +92,5 @@ LL | | unimplemented!()
LL | | }
| |_________^
-error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:410:9
- |
-LL | / pub fn new() -> impl PartialOrd {
-LL | | 0i32
-LL | | }
- | |_________^
-
-error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:421:9
- |
-LL | / pub fn new() -> X {
-LL | | 0i32
-LL | | }
- | |_________^
-
-error: aborting due to 14 previous errors
+error: aborting due to 12 previous errors
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs
new file mode 100644
index 000000000..7bc6fec10
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs
@@ -0,0 +1,26 @@
+#![feature(type_alias_impl_trait)]
+#![warn(clippy::new_ret_no_self)]
+
+mod issue10041 {
+ struct Bomb;
+
+ impl Bomb {
+ // Hidden <Rhs = Self> default generic parameter.
+ pub fn new() -> impl PartialOrd {
+ 0i32
+ }
+ }
+
+ // TAIT with self-referencing bounds
+ type X = impl std::ops::Add<Output = X>;
+
+ struct Bomb2;
+
+ impl Bomb2 {
+ pub fn new() -> X {
+ 0i32
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr
new file mode 100644
index 000000000..babb634fd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr
@@ -0,0 +1,9 @@
+error[E0275]: overflow evaluating the requirement `<i32 as std::ops::Add>::Output == issue10041::X`
+ --> $DIR/new_ret_no_self_overflow.rs:20:25
+ |
+LL | pub fn new() -> X {
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index 1e42e1fba..6a726941b 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -5,7 +5,8 @@
clippy::deref_addrof,
clippy::redundant_field_names,
clippy::uninlined_format_args,
- clippy::unnecessary_struct_initialization
+ clippy::unnecessary_struct_initialization,
+ clippy::useless_vec
)]
struct Unit;
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index f10f2bcf2..64edfc325 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -1,5 +1,5 @@
error: statement with no effect
- --> $DIR/no_effect.rs:97:5
+ --> $DIR/no_effect.rs:98:5
|
LL | 0;
| ^^
@@ -7,151 +7,151 @@ LL | 0;
= note: `-D clippy::no-effect` implied by `-D warnings`
error: statement with no effect
- --> $DIR/no_effect.rs:98:5
+ --> $DIR/no_effect.rs:99:5
|
LL | s2;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:99:5
+ --> $DIR/no_effect.rs:100:5
|
LL | Unit;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:100:5
+ --> $DIR/no_effect.rs:101:5
|
LL | Tuple(0);
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:101:5
+ --> $DIR/no_effect.rs:102:5
|
LL | Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:102:5
+ --> $DIR/no_effect.rs:103:5
|
LL | Struct { ..s };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:103:5
+ --> $DIR/no_effect.rs:104:5
|
LL | Union { a: 0 };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:104:5
+ --> $DIR/no_effect.rs:105:5
|
LL | Enum::Tuple(0);
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:105:5
+ --> $DIR/no_effect.rs:106:5
|
LL | Enum::Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:106:5
+ --> $DIR/no_effect.rs:107:5
|
LL | 5 + 6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:107:5
+ --> $DIR/no_effect.rs:108:5
|
LL | *&42;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:108:5
+ --> $DIR/no_effect.rs:109:5
|
LL | &6;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:109:5
+ --> $DIR/no_effect.rs:110:5
|
LL | (5, 6, 7);
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:110:5
+ --> $DIR/no_effect.rs:111:5
|
LL | ..;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:111:5
+ --> $DIR/no_effect.rs:112:5
|
LL | 5..;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:112:5
+ --> $DIR/no_effect.rs:113:5
|
LL | ..5;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:113:5
+ --> $DIR/no_effect.rs:114:5
|
LL | 5..6;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:114:5
+ --> $DIR/no_effect.rs:115:5
|
LL | 5..=6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:115:5
+ --> $DIR/no_effect.rs:116:5
|
LL | [42, 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:116:5
+ --> $DIR/no_effect.rs:117:5
|
LL | [42, 55][1];
| ^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:117:5
+ --> $DIR/no_effect.rs:118:5
|
LL | (42, 55).1;
| ^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:118:5
+ --> $DIR/no_effect.rs:119:5
|
LL | [42; 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:119:5
+ --> $DIR/no_effect.rs:120:5
|
LL | [42; 55][13];
| ^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:121:5
+ --> $DIR/no_effect.rs:122:5
|
LL | || x += 5;
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:123:5
+ --> $DIR/no_effect.rs:124:5
|
LL | FooString { s: s };
| ^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:124:5
+ --> $DIR/no_effect.rs:125:5
|
LL | let _unused = 1;
| ^^^^^^^^^^^^^^^^
@@ -159,19 +159,19 @@ LL | let _unused = 1;
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:125:5
+ --> $DIR/no_effect.rs:126:5
|
LL | let _penguin = || println!("Some helpful closure");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:126:5
+ --> $DIR/no_effect.rs:127:5
|
LL | let _duck = Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:127:5
+ --> $DIR/no_effect.rs:128:5
|
LL | let _cat = [2, 4, 6, 8][2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/no_effect_return.rs b/src/tools/clippy/tests/ui/no_effect_return.rs
new file mode 100644
index 000000000..231dd063a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect_return.rs
@@ -0,0 +1,81 @@
+#![allow(clippy::unused_unit, dead_code, unused)]
+#![no_main]
+
+use std::ops::ControlFlow;
+
+fn a() -> u32 {
+ {
+ 0u32;
+ }
+ 0
+}
+
+async fn b() -> u32 {
+ {
+ 0u32;
+ }
+ 0
+}
+
+type C = i32;
+async fn c() -> C {
+ {
+ 0i32 as C;
+ }
+ 0
+}
+
+fn d() -> u128 {
+ {
+ // not last stmt
+ 0u128;
+ println!("lol");
+ }
+ 0
+}
+
+fn e() -> u32 {
+ {
+ // mismatched types
+ 0u16;
+ }
+ 0
+}
+
+fn f() -> [u16; 1] {
+ {
+ [1u16];
+ }
+ [1]
+}
+
+fn g() -> ControlFlow<()> {
+ {
+ ControlFlow::Break::<()>(());
+ }
+ ControlFlow::Continue(())
+}
+
+fn h() -> Vec<u16> {
+ {
+ // function call, so this won't trigger `no_effect`. not an issue with this change, but the
+ // lint itself (but also not really.)
+ vec![0u16];
+ }
+ vec![]
+}
+
+fn i() -> () {
+ {
+ ();
+ }
+ ()
+}
+
+fn j() {
+ {
+ // does not suggest on function without explicit return type
+ ();
+ }
+ ()
+}
diff --git a/src/tools/clippy/tests/ui/no_effect_return.stderr b/src/tools/clippy/tests/ui/no_effect_return.stderr
new file mode 100644
index 000000000..779900e18
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect_return.stderr
@@ -0,0 +1,70 @@
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:8:9
+ |
+LL | 0u32;
+ | -^^^^
+ | |
+ | help: did you mean to return it?: `return`
+ |
+ = note: `-D clippy::no-effect` implied by `-D warnings`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:15:9
+ |
+LL | 0u32;
+ | -^^^^
+ | |
+ | help: did you mean to return it?: `return`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:23:9
+ |
+LL | 0i32 as C;
+ | -^^^^^^^^^
+ | |
+ | help: did you mean to return it?: `return`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:31:9
+ |
+LL | 0u128;
+ | ^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:40:9
+ |
+LL | 0u16;
+ | ^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:47:9
+ |
+LL | [1u16];
+ | -^^^^^^
+ | |
+ | help: did you mean to return it?: `return`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:54:9
+ |
+LL | ControlFlow::Break::<()>(());
+ | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: did you mean to return it?: `return`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:70:9
+ |
+LL | ();
+ | -^^
+ | |
+ | help: did you mean to return it?: `return`
+
+error: statement with no effect
+ --> $DIR/no_effect_return.rs:78:9
+ |
+LL | ();
+ | ^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs
index 583096ac0..987a4775e 100644
--- a/src/tools/clippy/tests/ui/non_expressive_names.rs
+++ b/src/tools/clippy/tests/ui/non_expressive_names.rs
@@ -25,9 +25,9 @@ impl MaybeInst {
}
fn underscores_and_numbers() {
- let _1 = 1; //~ERROR Consider a more descriptive name
- let ____1 = 1; //~ERROR Consider a more descriptive name
- let __1___2 = 12; //~ERROR Consider a more descriptive name
+ let _1 = 1; //~ERROR: consider choosing a more descriptive name
+ let ____1 = 1; //~ERROR: consider choosing a more descriptive name
+ let __1___2 = 12; //~ERROR: consider choosing a more descriptive name
let _1_ok = 1;
}
@@ -48,9 +48,9 @@ struct Bar;
impl Bar {
fn bar() {
- let _1 = 1;
- let ____1 = 1;
- let __1___2 = 12;
+ let _1 = 1; //~ERROR: consider choosing a more descriptive name
+ let ____1 = 1; //~ERROR: consider choosing a more descriptive name
+ let __1___2 = 12; //~ERROR: consider choosing a more descriptive name
let _1_ok = 1;
}
}
diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr
index 116d5da87..b62748d49 100644
--- a/src/tools/clippy/tests/ui/non_expressive_names.stderr
+++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr
@@ -1,7 +1,7 @@
error: consider choosing a more descriptive name
--> $DIR/non_expressive_names.rs:28:9
|
-LL | let _1 = 1; //~ERROR Consider a more descriptive name
+LL | let _1 = 1;
| ^^
|
= note: `-D clippy::just-underscores-and-digits` implied by `-D warnings`
@@ -9,13 +9,13 @@ LL | let _1 = 1; //~ERROR Consider a more descriptive name
error: consider choosing a more descriptive name
--> $DIR/non_expressive_names.rs:29:9
|
-LL | let ____1 = 1; //~ERROR Consider a more descriptive name
+LL | let ____1 = 1;
| ^^^^^
error: consider choosing a more descriptive name
--> $DIR/non_expressive_names.rs:30:9
|
-LL | let __1___2 = 12; //~ERROR Consider a more descriptive name
+LL | let __1___2 = 12;
| ^^^^^^^
error: consider choosing a more descriptive name
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
index 89d127528..5d0da8dce 100644
--- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
@@ -1,4 +1,4 @@
-//@ignore-windows
+//@ignore-target-windows
//@run-rustfix
#![warn(clippy::non_octal_unix_permissions)]
use std::fs::{DirBuilder, File, OpenOptions, Permissions};
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
index 1b3a322d7..04a364305 100644
--- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
@@ -1,4 +1,4 @@
-//@ignore-windows
+//@ignore-target-windows
//@run-rustfix
#![warn(clippy::non_octal_unix_permissions)]
use std::fs::{DirBuilder, File, OpenOptions, Permissions};
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index 80cc7c60f..e4aa0937b 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -1,6 +1,7 @@
#![feature(lint_reasons)]
-#![allow(unused, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)]
#![warn(clippy::nonminimal_bool)]
+#![allow(clippy::useless_vec)]
fn main() {
let a: bool = unimplemented!();
@@ -110,3 +111,17 @@ fn issue_10435() {
println!("{}", line!());
}
}
+
+fn issue10836() {
+ struct Foo(bool);
+ impl std::ops::Not for Foo {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ !self.0
+ }
+ }
+
+ // Should not lint
+ let _: bool = !!Foo(true);
+}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
index 91b5805aa..e2e4d6477 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -1,5 +1,5 @@
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:11:13
+ --> $DIR/nonminimal_bool.rs:12:13
|
LL | let _ = !true;
| ^^^^^ help: try: `false`
@@ -7,43 +7,43 @@ LL | let _ = !true;
= note: `-D clippy::nonminimal-bool` implied by `-D warnings`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:12:13
+ --> $DIR/nonminimal_bool.rs:13:13
|
LL | let _ = !false;
| ^^^^^^ help: try: `true`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:13:13
+ --> $DIR/nonminimal_bool.rs:14:13
|
LL | let _ = !!a;
| ^^^ help: try: `a`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:14:13
+ --> $DIR/nonminimal_bool.rs:15:13
|
LL | let _ = false || a;
| ^^^^^^^^^^ help: try: `a`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:18:13
+ --> $DIR/nonminimal_bool.rs:19:13
|
LL | let _ = !(!a && b);
| ^^^^^^^^^^ help: try: `a || !b`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:19:13
+ --> $DIR/nonminimal_bool.rs:20:13
|
LL | let _ = !(!a || b);
| ^^^^^^^^^^ help: try: `a && !b`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:20:13
+ --> $DIR/nonminimal_bool.rs:21:13
|
LL | let _ = !a && !(b && c);
| ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:28:13
+ --> $DIR/nonminimal_bool.rs:29:13
|
LL | let _ = a == b && c == 5 && a == b;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL | let _ = a == b && c == 5;
| ~~~~~~~~~~~~~~~~
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:29:13
+ --> $DIR/nonminimal_bool.rs:30:13
|
LL | let _ = a == b || c == 5 || a == b;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL | let _ = a == b || c == 5;
| ~~~~~~~~~~~~~~~~
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:30:13
+ --> $DIR/nonminimal_bool.rs:31:13
|
LL | let _ = a == b && c == 5 && b == a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,7 +82,7 @@ LL | let _ = a == b && c == 5;
| ~~~~~~~~~~~~~~~~
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:31:13
+ --> $DIR/nonminimal_bool.rs:32:13
|
LL | let _ = a != b || !(a != b || c == d);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,7 +95,7 @@ LL | let _ = a != b || c != d;
| ~~~~~~~~~~~~~~~~
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:32:13
+ --> $DIR/nonminimal_bool.rs:33:13
|
LL | let _ = a != b && !(a != b && c == d);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL | let _ = a != b && c != d;
| ~~~~~~~~~~~~~~~~
error: this boolean expression can be simplified
- --> $DIR/nonminimal_bool.rs:62:8
+ --> $DIR/nonminimal_bool.rs:63:8
|
LL | if matches!(true, true) && true {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)`
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
index 05802a2c8..294f2aa48 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(unused, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)]
#![warn(clippy::nonminimal_bool)]
fn methods_with_negation() {
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
index cd5b576fa..a165368ab 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(unused, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)]
#![warn(clippy::nonminimal_bool)]
fn methods_with_negation() {
diff --git a/src/tools/clippy/tests/ui/octal_escapes.stderr b/src/tools/clippy/tests/ui/octal_escapes.stderr
index aa362e963..63fdfe486 100644
--- a/src/tools/clippy/tests/ui/octal_escapes.stderr
+++ b/src/tools/clippy/tests/ui/octal_escapes.stderr
@@ -34,17 +34,17 @@ LL | let _bad2 = b"/x0033[0m";
error: octal-looking escape in string literal
--> $DIR/octal_escapes.rs:6:17
|
-LL | let _bad3 = "//033[0m";
+LL | let _bad3 = "///033[0m";
| ^^^^^^^^^^^
|
= help: octal escapes are not supported, `/0` is always a null character
help: if an octal escape was intended, use the hexadecimal representation instead
|
-LL | let _bad3 = "//x1b[0m";
+LL | let _bad3 = "///x1b[0m";
| ~~~~~~~~~~~
help: if the null character is intended, disambiguate using
|
-LL | let _bad3 = "//x0033[0m";
+LL | let _bad3 = "///x0033[0m";
| ~~~~~~~~~~~~~
error: octal-looking escape in string literal
diff --git a/src/tools/clippy/tests/ui/ok_expect.rs b/src/tools/clippy/tests/ui/ok_expect.rs
index ff68d38c7..2047ee689 100644
--- a/src/tools/clippy/tests/ui/ok_expect.rs
+++ b/src/tools/clippy/tests/ui/ok_expect.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::unnecessary_literal_unwrap)]
+
use std::io;
struct MyError(()); // doesn't implement Debug
diff --git a/src/tools/clippy/tests/ui/ok_expect.stderr b/src/tools/clippy/tests/ui/ok_expect.stderr
index 6c40adbb5..ab9df26eb 100644
--- a/src/tools/clippy/tests/ui/ok_expect.stderr
+++ b/src/tools/clippy/tests/ui/ok_expect.stderr
@@ -1,5 +1,5 @@
error: called `ok().expect()` on a `Result` value
- --> $DIR/ok_expect.rs:14:5
+ --> $DIR/ok_expect.rs:16:5
|
LL | res.ok().expect("disaster!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | res.ok().expect("disaster!");
= note: `-D clippy::ok-expect` implied by `-D warnings`
error: called `ok().expect()` on a `Result` value
- --> $DIR/ok_expect.rs:20:5
+ --> $DIR/ok_expect.rs:22:5
|
LL | res3.ok().expect("whoof");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | res3.ok().expect("whoof");
= help: you can call `expect()` directly on the `Result`
error: called `ok().expect()` on a `Result` value
- --> $DIR/ok_expect.rs:22:5
+ --> $DIR/ok_expect.rs:24:5
|
LL | res4.ok().expect("argh");
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | res4.ok().expect("argh");
= help: you can call `expect()` directly on the `Result`
error: called `ok().expect()` on a `Result` value
- --> $DIR/ok_expect.rs:24:5
+ --> $DIR/ok_expect.rs:26:5
|
LL | res5.ok().expect("oops");
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL | res5.ok().expect("oops");
= help: you can call `expect()` directly on the `Result`
error: called `ok().expect()` on a `Result` value
- --> $DIR/ok_expect.rs:26:5
+ --> $DIR/ok_expect.rs:28:5
|
LL | res6.ok().expect("meh");
| ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
index e1c0fa3f7..4d1a6a1ab 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused, clippy::redundant_clone)]
+#![allow(unused, clippy::redundant_clone, clippy::useless_vec)]
#![warn(clippy::option_as_ref_deref)]
use std::ffi::{CString, OsString};
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
index 6f4917fd1..66d5a1250 100644
--- a/src/tools/clippy/tests/ui/option_as_ref_deref.rs
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused, clippy::redundant_clone)]
+#![allow(unused, clippy::redundant_clone, clippy::useless_vec)]
#![warn(clippy::option_as_ref_deref)]
use std::ffi::{CString, OsString};
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs
index ee1fe3f1f..65a1b467f 100644
--- a/src/tools/clippy/tests/ui/option_env_unwrap.rs
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::option_env_unwrap)]
#![allow(clippy::map_flatten)]
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 2b8ce5477..8e59e4375 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.fixed
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -208,3 +208,19 @@ fn issue9742() -> Option<&'static str> {
_ => None,
}
}
+
+mod issue10729 {
+ #![allow(clippy::unit_arg, dead_code)]
+
+ pub fn reproduce(initial: &Option<String>) {
+ // 👇 needs `.as_ref()` because initial is an `&Option<_>`
+ initial.as_ref().map_or({}, |value| do_something(value))
+ }
+
+ pub fn reproduce2(initial: &mut Option<String>) {
+ initial.as_mut().map_or({}, |value| do_something2(value))
+ }
+
+ fn do_something(_value: &str) {}
+ fn do_something2(_value: &mut str) {}
+}
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 cfbec8cb2..e72edf2a8 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.rs
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -249,3 +249,25 @@ fn issue9742() -> Option<&'static str> {
_ => None,
}
}
+
+mod issue10729 {
+ #![allow(clippy::unit_arg, dead_code)]
+
+ pub fn reproduce(initial: &Option<String>) {
+ // 👇 needs `.as_ref()` because initial is an `&Option<_>`
+ match initial {
+ Some(value) => do_something(value),
+ None => {},
+ }
+ }
+
+ pub fn reproduce2(initial: &mut Option<String>) {
+ match initial {
+ Some(value) => do_something2(value),
+ None => {},
+ }
+ }
+
+ fn do_something(_value: &str) {}
+ fn do_something2(_value: &mut str) {}
+}
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 91d52fc79..aa2da2174 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.stderr
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -271,5 +271,23 @@ error: use Option::map_or instead of an if let/else
LL | let _ = if let Ok(a) = res { a + 1 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
-error: aborting due to 21 previous errors
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:258:9
+ |
+LL | / match initial {
+LL | | Some(value) => do_something(value),
+LL | | None => {},
+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
+ |
+LL | / match initial {
+LL | | Some(value) => do_something2(value),
+LL | | None => {},
+LL | | }
+ | |_________^ help: try: `initial.as_mut().map_or({}, |value| do_something2(value))`
+
+error: aborting due to 23 previous errors
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
index f723a55f7..703debb7a 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -1,7 +1,13 @@
//@run-rustfix
#![warn(clippy::or_fun_call)]
#![allow(dead_code)]
-#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
+#![allow(
+ clippy::borrow_as_ptr,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_wraps,
+ clippy::unnecessary_literal_unwrap,
+ clippy::useless_vec
+)]
use std::collections::BTreeMap;
use std::collections::HashMap;
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
index 61ef6e27f..bb86fe0d4 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.rs
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -1,7 +1,13 @@
//@run-rustfix
#![warn(clippy::or_fun_call)]
#![allow(dead_code)]
-#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
+#![allow(
+ clippy::borrow_as_ptr,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_wraps,
+ clippy::unnecessary_literal_unwrap,
+ clippy::useless_vec
+)]
use std::collections::BTreeMap;
use std::collections::HashMap;
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
index ba3001db7..0b5c686be 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -1,5 +1,5 @@
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:48:22
+ --> $DIR/or_fun_call.rs:54:22
|
LL | with_constructor.unwrap_or(make());
| ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
@@ -7,163 +7,163 @@ LL | with_constructor.unwrap_or(make());
= note: `-D clippy::or-fun-call` implied by `-D warnings`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:51:14
+ --> $DIR/or_fun_call.rs:57:14
|
LL | with_new.unwrap_or(Vec::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:54:21
+ --> $DIR/or_fun_call.rs:60:21
|
LL | with_const_args.unwrap_or(Vec::with_capacity(12));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:57:14
+ --> $DIR/or_fun_call.rs:63:14
|
LL | with_err.unwrap_or(make());
| ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:60:19
+ --> $DIR/or_fun_call.rs:66:19
|
LL | with_err_args.unwrap_or(Vec::with_capacity(12));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:63:24
+ --> $DIR/or_fun_call.rs:69:24
|
LL | with_default_trait.unwrap_or(Default::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:66:23
+ --> $DIR/or_fun_call.rs:72:23
|
LL | with_default_type.unwrap_or(u64::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:69:18
+ --> $DIR/or_fun_call.rs:75:18
|
LL | self_default.unwrap_or(<FakeDefault>::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:72:18
+ --> $DIR/or_fun_call.rs:78:18
|
LL | real_default.unwrap_or(<FakeDefault as Default>::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:75:14
+ --> $DIR/or_fun_call.rs:81:14
|
LL | with_vec.unwrap_or(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:78:21
+ --> $DIR/or_fun_call.rs:84:21
|
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
error: use of `or_insert` followed by a call to `new`
- --> $DIR/or_fun_call.rs:81:19
+ --> $DIR/or_fun_call.rs:87:19
|
LL | map.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
error: use of `or_insert` followed by a call to `new`
- --> $DIR/or_fun_call.rs:84:23
+ --> $DIR/or_fun_call.rs:90:23
|
LL | map_vec.entry(42).or_insert(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
error: use of `or_insert` followed by a call to `new`
- --> $DIR/or_fun_call.rs:87:21
+ --> $DIR/or_fun_call.rs:93:21
|
LL | btree.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
error: use of `or_insert` followed by a call to `new`
- --> $DIR/or_fun_call.rs:90:25
+ --> $DIR/or_fun_call.rs:96:25
|
LL | btree_vec.entry(42).or_insert(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:93:21
+ --> $DIR/or_fun_call.rs:99:21
|
LL | let _ = stringy.unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:101:21
+ --> $DIR/or_fun_call.rs:107:21
|
LL | let _ = Some(1).unwrap_or(map[&1]);
| ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:103:21
+ --> $DIR/or_fun_call.rs:109:21
|
LL | let _ = Some(1).unwrap_or(map[&1]);
| ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
error: use of `or` followed by a function call
- --> $DIR/or_fun_call.rs:127:35
+ --> $DIR/or_fun_call.rs:133:35
|
LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:166:14
+ --> $DIR/or_fun_call.rs:172:14
|
LL | None.unwrap_or(ptr_to_ref(s));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:172:14
+ --> $DIR/or_fun_call.rs:178:14
|
LL | None.unwrap_or(unsafe { ptr_to_ref(s) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:174:14
+ --> $DIR/or_fun_call.rs:180:14
|
LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:188:14
+ --> $DIR/or_fun_call.rs:194:14
|
LL | .unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:201:14
+ --> $DIR/or_fun_call.rs:207:14
|
LL | .unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:213:14
+ --> $DIR/or_fun_call.rs:219:14
|
LL | .unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:224:10
+ --> $DIR/or_fun_call.rs:230:10
|
LL | .unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `map_or` followed by a function call
- --> $DIR/or_fun_call.rs:249:25
+ --> $DIR/or_fun_call.rs:255:25
|
LL | let _ = Some(4).map_or(g(), |v| v);
| ^^^^^^^^^^^^^^^^^^ help: try this: `map_or_else(g, |v| v)`
error: use of `map_or` followed by a function call
- --> $DIR/or_fun_call.rs:250:25
+ --> $DIR/or_fun_call.rs:256:25
|
LL | let _ = Some(4).map_or(g(), f);
| ^^^^^^^^^^^^^^ help: try this: `map_or_else(g, f)`
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
index 40badac44..773dfc3c5 100644
--- a/src/tools/clippy/tests/ui/or_then_unwrap.fixed
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity, clippy::let_unit_value)]
+#![allow(clippy::map_identity, clippy::let_unit_value, clippy::unnecessary_literal_unwrap)]
struct SomeStruct;
impl SomeStruct {
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs
index 76c9942fe..5867e0148 100644
--- a/src/tools/clippy/tests/ui/or_then_unwrap.rs
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity, clippy::let_unit_value)]
+#![allow(clippy::map_identity, clippy::let_unit_value, clippy::unnecessary_literal_unwrap)]
struct SomeStruct;
impl SomeStruct {
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
index e1e301140..14a6b98d0 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.rs
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
@@ -1,4 +1,5 @@
#![warn(clippy::overflow_check_conditional)]
+#![allow(clippy::needless_if)]
fn test(a: u32, b: u32, c: u32) {
if a + b < a {}
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
index 92d1d8ef9..3ec2298f8 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
@@ -1,5 +1,5 @@
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:4:8
+ --> $DIR/overflow_check_conditional.rs:5:8
|
LL | if a + b < a {}
| ^^^^^^^^^
@@ -7,43 +7,43 @@ LL | if a + b < a {}
= note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:5:8
+ --> $DIR/overflow_check_conditional.rs:6:8
|
LL | if a > a + b {}
| ^^^^^^^^^
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:6:8
+ --> $DIR/overflow_check_conditional.rs:7:8
|
LL | if a + b < b {}
| ^^^^^^^^^
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:7:8
+ --> $DIR/overflow_check_conditional.rs:8:8
|
LL | if b > a + b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:8:8
+ --> $DIR/overflow_check_conditional.rs:9:8
|
LL | if a - b > b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:9:8
+ --> $DIR/overflow_check_conditional.rs:10:8
|
LL | if b < a - b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:10:8
+ --> $DIR/overflow_check_conditional.rs:11:8
|
LL | if a - b > a {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:11:8
+ --> $DIR/overflow_check_conditional.rs:12:8
|
LL | if a < a - b {}
| ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.fixed b/src/tools/clippy/tests/ui/partialeq_to_none.fixed
index 2df87a26d..95e184b1d 100644
--- a/src/tools/clippy/tests/ui/partialeq_to_none.fixed
+++ b/src/tools/clippy/tests/ui/partialeq_to_none.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::partialeq_to_none)]
-#![allow(clippy::eq_op)]
+#![allow(clippy::eq_op, clippy::needless_if)]
struct Foobar;
diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.rs b/src/tools/clippy/tests/ui/partialeq_to_none.rs
index df6233b9a..4fa50dcc1 100644
--- a/src/tools/clippy/tests/ui/partialeq_to_none.rs
+++ b/src/tools/clippy/tests/ui/partialeq_to_none.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::partialeq_to_none)]
-#![allow(clippy::eq_op)]
+#![allow(clippy::eq_op, clippy::needless_if)]
struct Foobar;
diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed
index a1da47d84..714143e75 100644
--- a/src/tools/clippy/tests/ui/patterns.fixed
+++ b/src/tools/clippy/tests/ui/patterns.fixed
@@ -1,8 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::all)]
#![allow(unused)]
#![allow(clippy::uninlined_format_args)]
+#[macro_use]
+extern crate proc_macros;
+
fn main() {
let v = Some(true);
let s = [0, 1, 2, 3, 4];
@@ -34,4 +38,11 @@ fn main() {
ref x => println!("vec: {:?}", x),
ref y if y == &vec![0] => (),
}
+ external! {
+ let v = Some(true);
+ match v {
+ Some(x) => (),
+ y @ _ => (),
+ }
+ }
}
diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs
index 399066b81..153e26407 100644
--- a/src/tools/clippy/tests/ui/patterns.rs
+++ b/src/tools/clippy/tests/ui/patterns.rs
@@ -1,8 +1,12 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::all)]
#![allow(unused)]
#![allow(clippy::uninlined_format_args)]
+#[macro_use]
+extern crate proc_macros;
+
fn main() {
let v = Some(true);
let s = [0, 1, 2, 3, 4];
@@ -34,4 +38,11 @@ fn main() {
ref x @ _ => println!("vec: {:?}", x),
ref y if y == &vec![0] => (),
}
+ external! {
+ let v = Some(true);
+ match v {
+ Some(x) => (),
+ y @ _ => (),
+ }
+ }
}
diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr
index 2c46b4eb5..276330d21 100644
--- a/src/tools/clippy/tests/ui/patterns.stderr
+++ b/src/tools/clippy/tests/ui/patterns.stderr
@@ -1,5 +1,5 @@
error: the `y @ _` pattern can be written as just `y`
- --> $DIR/patterns.rs:11:9
+ --> $DIR/patterns.rs:15:9
|
LL | y @ _ => (),
| ^^^^^ help: try: `y`
@@ -7,13 +7,13 @@ LL | y @ _ => (),
= note: `-D clippy::redundant-pattern` implied by `-D warnings`
error: the `x @ _` pattern can be written as just `x`
- --> $DIR/patterns.rs:26:9
+ --> $DIR/patterns.rs:30:9
|
LL | ref mut x @ _ => {
| ^^^^^^^^^^^^^ help: try: `ref mut x`
error: the `x @ _` pattern can be written as just `x`
- --> $DIR/patterns.rs:34:9
+ --> $DIR/patterns.rs:38:9
|
LL | ref x @ _ => println!("vec: {:?}", x),
| ^^^^^^^^^ help: try: `ref x`
diff --git a/src/tools/clippy/tests/ui/print_with_newline.fixed b/src/tools/clippy/tests/ui/print_with_newline.fixed
new file mode 100644
index 000000000..6098dea39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_with_newline.fixed
@@ -0,0 +1,58 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+//@run-rustfix
+
+#![allow(clippy::print_literal)]
+#![warn(clippy::print_with_newline)]
+
+fn main() {
+ println!("Hello");
+ println!("Hello {}", "world");
+ println!("Hello {} {}", "world", "#2");
+ println!("{}", 1265);
+ println!();
+
+ // these are all fine
+ print!("");
+ print!("Hello");
+ println!("Hello");
+ println!("Hello\n");
+ println!("Hello {}\n", "world");
+ print!("Issue\n{}", 1265);
+ print!("{}", 1265);
+ print!("\n{}", 1275);
+ print!("\n\n");
+ print!("like eof\n\n");
+ print!("Hello {} {}\n\n", "world", "#2");
+ println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ println!("\nbla\n\n"); // #3126
+
+ // Escaping
+ print!("\\n"); // #3514
+ println!("\\"); // should fail
+ print!("\\\\n");
+
+ // Raw strings
+ print!(r"\n"); // #3778
+
+ // Literal newlines should also fail
+ println!(
+
+ );
+ println!(
+
+ );
+
+ // Don't warn on CRLF (#4208)
+ print!("\r\n");
+ print!("foo\r\n");
+ println!("\\r"); // should fail
+ print!("foo\rbar\n");
+
+ // Ignore expanded format strings
+ macro_rules! newline {
+ () => {
+ "\n"
+ };
+ }
+ print!(newline!());
+}
diff --git a/src/tools/clippy/tests/ui/print_with_newline.rs b/src/tools/clippy/tests/ui/print_with_newline.rs
index ff79ca75f..d9c7acc27 100644
--- a/src/tools/clippy/tests/ui/print_with_newline.rs
+++ b/src/tools/clippy/tests/ui/print_with_newline.rs
@@ -47,7 +47,7 @@ fn main() {
// Don't warn on CRLF (#4208)
print!("\r\n");
print!("foo\r\n");
- print!("\\r\n"); //~ ERROR
+ print!("\\r\n"); // should fail
print!("foo\rbar\n");
// Ignore expanded format strings
diff --git a/src/tools/clippy/tests/ui/print_with_newline.stderr b/src/tools/clippy/tests/ui/print_with_newline.stderr
index b9f5675fa..b97711e77 100644
--- a/src/tools/clippy/tests/ui/print_with_newline.stderr
+++ b/src/tools/clippy/tests/ui/print_with_newline.stderr
@@ -62,13 +62,13 @@ LL + println!();
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:31:5
|
-LL | print!("//n"); // should fail
+LL | print!("///n"); // should fail
| ^^^^^^^^^^^^^^
|
help: use `println!` instead
|
-LL - print!("//n"); // should fail
-LL + println!("/"); // should fail
+LL - print!("///n"); // should fail
+LL + println!("//"); // should fail
|
error: using `print!()` with a format string that ends in a single newline
@@ -104,13 +104,13 @@ LL ~
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:50:5
|
-LL | print!("/r/n"); //~ ERROR
+LL | print!("//r/n"); // should fail
| ^^^^^^^^^^^^^^^
|
help: use `println!` instead
|
-LL - print!("/r/n"); //~ ERROR
-LL + println!("/r"); //~ ERROR
+LL - print!("//r/n"); // should fail
+LL + println!("//r"); // should fail
|
error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/proc_macro.rs b/src/tools/clippy/tests/ui/proc_macro.rs
index 59914b8b8..b77874034 100644
--- a/src/tools/clippy/tests/ui/proc_macro.rs
+++ b/src/tools/clippy/tests/ui/proc_macro.rs
@@ -1,5 +1,4 @@
//! Check that we correctly lint procedural macros.
-#![crate_type = "proc-macro"]
extern crate proc_macro;
diff --git a/src/tools/clippy/tests/ui/proc_macro.stderr b/src/tools/clippy/tests/ui/proc_macro.stderr
index c795f6ad0..d912b5027 100644
--- a/src/tools/clippy/tests/ui/proc_macro.stderr
+++ b/src/tools/clippy/tests/ui/proc_macro.stderr
@@ -1,5 +1,5 @@
error: approximate value of `f{32, 64}::consts::PI` found
- --> $DIR/proc_macro.rs:10:14
+ --> $DIR/proc_macro.rs:9:14
|
LL | let _x = 3.14;
| ^^^^
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index 5f54101ca..709f74ee6 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -1,5 +1,10 @@
#![feature(lint_reasons)]
-#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#![allow(
+ unused,
+ clippy::many_single_char_names,
+ clippy::needless_lifetimes,
+ clippy::redundant_clone
+)]
#![warn(clippy::ptr_arg)]
use std::borrow::Cow;
@@ -235,3 +240,29 @@ fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
takes_dyn(b);
takes_dyn(c);
}
+
+mod issue_9218 {
+ use std::borrow::Cow;
+
+ fn cow_non_elided_lifetime<'a>(input: &Cow<'a, str>) -> &'a str {
+ todo!()
+ }
+
+ // This one has an anonymous lifetime so it's not okay
+ fn cow_elided_lifetime<'a>(input: &'a Cow<str>) -> &'a str {
+ todo!()
+ }
+
+ // These two's return types don't use use 'a so it's not okay
+ fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str {
+ todo!()
+ }
+ fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str {
+ todo!()
+ }
+
+ // Inferred to be `&'a str`, afaik.
+ fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
+ todo!()
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 6b4de98ce..d663b070b 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -1,5 +1,5 @@
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:8:14
+ --> $DIR/ptr_arg.rs:13:14
|
LL | fn do_vec(x: &Vec<i64>) {
| ^^^^^^^^^ help: change this to: `&[i64]`
@@ -7,43 +7,43 @@ LL | fn do_vec(x: &Vec<i64>) {
= note: `-D clippy::ptr-arg` implied by `-D warnings`
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:12:18
+ --> $DIR/ptr_arg.rs:17:18
|
LL | fn do_vec_mut(x: &mut Vec<i64>) {
| ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:16:14
+ --> $DIR/ptr_arg.rs:21:14
|
LL | fn do_str(x: &String) {
| ^^^^^^^ help: change this to: `&str`
error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:20:18
+ --> $DIR/ptr_arg.rs:25:18
|
LL | fn do_str_mut(x: &mut String) {
| ^^^^^^^^^^^ help: change this to: `&mut str`
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:24:15
+ --> $DIR/ptr_arg.rs:29:15
|
LL | fn do_path(x: &PathBuf) {
| ^^^^^^^^ help: change this to: `&Path`
error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:28:19
+ --> $DIR/ptr_arg.rs:33:19
|
LL | fn do_path_mut(x: &mut PathBuf) {
| ^^^^^^^^^^^^ help: change this to: `&mut Path`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:36:18
+ --> $DIR/ptr_arg.rs:41:18
|
LL | fn do_vec(x: &Vec<i64>);
| ^^^^^^^^^ help: change this to: `&[i64]`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:49:14
+ --> $DIR/ptr_arg.rs:54:14
|
LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
| ^^^^^^^^
@@ -60,7 +60,7 @@ LL ~ x.to_owned()
|
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:58:18
+ --> $DIR/ptr_arg.rs:63:18
|
LL | fn str_cloned(x: &String) -> String {
| ^^^^^^^
@@ -76,7 +76,7 @@ LL ~ x.to_owned()
|
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:66:19
+ --> $DIR/ptr_arg.rs:71:19
|
LL | fn path_cloned(x: &PathBuf) -> PathBuf {
| ^^^^^^^^
@@ -92,7 +92,7 @@ LL ~ x.to_path_buf()
|
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:74:44
+ --> $DIR/ptr_arg.rs:79:44
|
LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
| ^^^^^^^
@@ -106,19 +106,19 @@ LL ~ let c = y;
|
error: using a reference to `Cow` is not recommended
- --> $DIR/ptr_arg.rs:88:25
+ --> $DIR/ptr_arg.rs:93:25
|
LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
| ^^^^^^^^^^^ help: change this to: `&[i32]`
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:117:66
+ --> $DIR/ptr_arg.rs:122:66
|
LL | fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
| ^^^^^^^ help: change this to: `&str`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:146:21
+ --> $DIR/ptr_arg.rs:151:21
|
LL | fn foo_vec(vec: &Vec<u8>) {
| ^^^^^^^^
@@ -131,7 +131,7 @@ LL ~ let _ = vec.to_owned().clone();
|
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:151:23
+ --> $DIR/ptr_arg.rs:156:23
|
LL | fn foo_path(path: &PathBuf) {
| ^^^^^^^^
@@ -144,7 +144,7 @@ LL ~ let _ = path.to_path_buf().clone();
|
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:156:21
+ --> $DIR/ptr_arg.rs:161:21
|
LL | fn foo_str(str: &PathBuf) {
| ^^^^^^^^
@@ -157,28 +157,46 @@ LL ~ let _ = str.to_path_buf().clone();
|
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:162:29
+ --> $DIR/ptr_arg.rs:167:29
|
LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
| ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:224:17
+ --> $DIR/ptr_arg.rs:229:17
|
LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
| ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:224:35
+ --> $DIR/ptr_arg.rs:229:35
|
LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
| ^^^^^^^^^^^ help: change this to: `&mut str`
error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:224:51
+ --> $DIR/ptr_arg.rs:229:51
|
LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
| ^^^^^^^^^^^^ help: change this to: `&mut Path`
-error: aborting due to 20 previous errors
+error: using a reference to `Cow` is not recommended
+ --> $DIR/ptr_arg.rs:252:39
+ |
+LL | fn cow_elided_lifetime<'a>(input: &'a Cow<str>) -> &'a str {
+ | ^^^^^^^^^^^^ help: change this to: `&str`
+
+error: using a reference to `Cow` is not recommended
+ --> $DIR/ptr_arg.rs:257:36
+ |
+LL | fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str {
+ | ^^^^^^^^^^^^^^^^ help: change this to: `&str`
+
+error: using a reference to `Cow` is not recommended
+ --> $DIR/ptr_arg.rs:260:40
+ |
+LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str {
+ | ^^^^^^^^^^^^^^^^ help: change this to: `&str`
+
+error: aborting due to 23 previous errors
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index 2c2567d67..26a64c861 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::ptr_as_ptr)]
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index 6000e5c08..ea40d4947 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::ptr_as_ptr)]
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
new file mode 100644
index 000000000..1ef1809d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
@@ -0,0 +1,62 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+
+#![warn(clippy::ptr_cast_constness)]
+#![allow(clippy::transmute_ptr_to_ref, clippy::unnecessary_cast, unused)]
+
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+
+unsafe fn ptr_to_ref<T, U>(p: *const T, om: *mut U) {
+ let _: &mut T = std::mem::transmute(p.cast_mut());
+ let _ = &mut *p.cast_mut();
+ let _: &T = &*(om as *const T);
+}
+
+#[inline_macros]
+fn main() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr as *const u32;
+ let _ = mut_ptr as *mut u32;
+
+ // Make sure the lint can handle the difference in their operator precedences.
+ unsafe {
+ let ptr_ptr: *const *const u32 = &ptr;
+ let _ = (*ptr_ptr).cast_mut();
+ }
+
+ let _ = ptr.cast_mut();
+ let _ = mut_ptr.cast_const();
+
+ // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized
+ let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
+ let _ = ptr_of_array as *const [u32];
+ let _ = ptr_of_array as *const dyn std::fmt::Debug;
+
+ // Make sure the lint is triggered inside a macro
+ let _ = inline!($ptr as *const u32);
+
+ // Do not lint inside macros from external crates
+ let _ = external!($ptr as *const u32);
+}
+
+#[clippy::msrv = "1.64"]
+fn _msrv_1_64() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this
+ let _ = ptr as *mut u32;
+ let _ = mut_ptr as *const u32;
+}
+
+#[clippy::msrv = "1.65"]
+fn _msrv_1_65() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr.cast_mut();
+ let _ = mut_ptr.cast_const();
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
new file mode 100644
index 000000000..2c15cd429
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
@@ -0,0 +1,62 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+
+#![warn(clippy::ptr_cast_constness)]
+#![allow(clippy::transmute_ptr_to_ref, clippy::unnecessary_cast, unused)]
+
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+
+unsafe fn ptr_to_ref<T, U>(p: *const T, om: *mut U) {
+ let _: &mut T = std::mem::transmute(p as *mut T);
+ let _ = &mut *(p as *mut T);
+ let _: &T = &*(om as *const T);
+}
+
+#[inline_macros]
+fn main() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr as *const u32;
+ let _ = mut_ptr as *mut u32;
+
+ // Make sure the lint can handle the difference in their operator precedences.
+ unsafe {
+ let ptr_ptr: *const *const u32 = &ptr;
+ let _ = *ptr_ptr as *mut u32;
+ }
+
+ let _ = ptr as *mut u32;
+ let _ = mut_ptr as *const u32;
+
+ // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized
+ let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
+ let _ = ptr_of_array as *const [u32];
+ let _ = ptr_of_array as *const dyn std::fmt::Debug;
+
+ // Make sure the lint is triggered inside a macro
+ let _ = inline!($ptr as *const u32);
+
+ // Do not lint inside macros from external crates
+ let _ = external!($ptr as *const u32);
+}
+
+#[clippy::msrv = "1.64"]
+fn _msrv_1_64() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this
+ let _ = ptr as *mut u32;
+ let _ = mut_ptr as *const u32;
+}
+
+#[clippy::msrv = "1.65"]
+fn _msrv_1_65() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr as *mut u32;
+ let _ = mut_ptr as *const u32;
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
new file mode 100644
index 000000000..0c3ff8636
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
@@ -0,0 +1,46 @@
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:11:41
+ |
+LL | let _: &mut T = std::mem::transmute(p as *mut T);
+ | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()`
+ |
+ = note: `-D clippy::ptr-cast-constness` implied by `-D warnings`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:12:19
+ |
+LL | let _ = &mut *(p as *mut T);
+ | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:27:17
+ |
+LL | let _ = *ptr_ptr as *mut u32;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:30:13
+ |
+LL | let _ = ptr as *mut u32;
+ | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:31:13
+ |
+LL | let _ = mut_ptr as *const u32;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:60:13
+ |
+LL | let _ = ptr as *mut u32;
+ | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()`
+
+error: `as` casting between raw pointers while changing only its constness
+ --> $DIR/ptr_cast_constness.rs:61:13
+ |
+LL | let _ = mut_ptr as *const u32;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
index f69bc1318..6ffa401d7 100644
--- a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(clippy::unnecessary_cast)]
+#![allow(clippy::unnecessary_cast, clippy::useless_vec)]
fn main() {
let vec = vec![b'a', b'b', b'c'];
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
index eae36c277..de1f86cb8 100644
--- a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(clippy::unnecessary_cast)]
+#![allow(clippy::unnecessary_cast, clippy::useless_vec)]
fn main() {
let vec = vec![b'a', b'b', b'c'];
diff --git a/src/tools/clippy/tests/ui/pub_with_shorthand.fixed b/src/tools/clippy/tests/ui/pub_with_shorthand.fixed
new file mode 100644
index 000000000..a774faa0a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_with_shorthand.fixed
@@ -0,0 +1,38 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(clippy::needless_pub_self, unused)]
+#![warn(clippy::pub_with_shorthand)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+pub(in self) fn a() {}
+pub(in self) fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(in super) fn d() {}
+ pub(in super) fn e() {}
+ pub(in self) fn f() {}
+ pub(in crate) fn k() {}
+ pub(in crate) fn m() {}
+ mod b {
+ pub(in crate::a) fn l() {}
+ }
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/pub_with_shorthand.rs b/src/tools/clippy/tests/ui/pub_with_shorthand.rs
new file mode 100644
index 000000000..4a4bbc187
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_with_shorthand.rs
@@ -0,0 +1,38 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(clippy::needless_pub_self, unused)]
+#![warn(clippy::pub_with_shorthand)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+pub(self) fn a() {}
+pub(in self) fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(in super) fn d() {}
+ pub(super) fn e() {}
+ pub(self) fn f() {}
+ pub(crate) fn k() {}
+ pub(in crate) fn m() {}
+ mod b {
+ pub(in crate::a) fn l() {}
+ }
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/pub_with_shorthand.stderr b/src/tools/clippy/tests/ui/pub_with_shorthand.stderr
new file mode 100644
index 000000000..323b5a23b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_with_shorthand.stderr
@@ -0,0 +1,28 @@
+error: usage of `pub` without `in`
+ --> $DIR/pub_with_shorthand.rs:13:1
+ |
+LL | pub(self) fn a() {}
+ | ^^^^^^^^^ help: add it: `pub(in self)`
+ |
+ = note: `-D clippy::pub-with-shorthand` implied by `-D warnings`
+
+error: usage of `pub` without `in`
+ --> $DIR/pub_with_shorthand.rs:19:5
+ |
+LL | pub(super) fn e() {}
+ | ^^^^^^^^^^ help: add it: `pub(in super)`
+
+error: usage of `pub` without `in`
+ --> $DIR/pub_with_shorthand.rs:20:5
+ |
+LL | pub(self) fn f() {}
+ | ^^^^^^^^^ help: add it: `pub(in self)`
+
+error: usage of `pub` without `in`
+ --> $DIR/pub_with_shorthand.rs:21:5
+ |
+LL | pub(crate) fn k() {}
+ | ^^^^^^^^^^ help: add it: `pub(in crate)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pub_without_shorthand.fixed b/src/tools/clippy/tests/ui/pub_without_shorthand.fixed
new file mode 100644
index 000000000..fdb49ac4d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_without_shorthand.fixed
@@ -0,0 +1,38 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(clippy::needless_pub_self, unused)]
+#![warn(clippy::pub_without_shorthand)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+pub(self) fn a() {}
+pub(self) fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(super) fn d() {}
+ pub(super) fn e() {}
+ pub(self) fn f() {}
+ pub(crate) fn k() {}
+ pub(crate) fn m() {}
+ mod b {
+ pub(in crate::a) fn l() {}
+ }
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/pub_without_shorthand.rs b/src/tools/clippy/tests/ui/pub_without_shorthand.rs
new file mode 100644
index 000000000..1f2ef7ece
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_without_shorthand.rs
@@ -0,0 +1,38 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![feature(custom_inner_attributes)]
+#![allow(clippy::needless_pub_self, unused)]
+#![warn(clippy::pub_without_shorthand)]
+#![no_main]
+#![rustfmt::skip] // rustfmt will remove `in`, understandable
+ // but very annoying for our purposes!
+
+#[macro_use]
+extern crate proc_macros;
+
+pub(self) fn a() {}
+pub(in self) fn b() {}
+
+pub fn c() {}
+mod a {
+ pub(in super) fn d() {}
+ pub(super) fn e() {}
+ pub(self) fn f() {}
+ pub(crate) fn k() {}
+ pub(in crate) fn m() {}
+ mod b {
+ pub(in crate::a) fn l() {}
+ }
+}
+
+external! {
+ pub(self) fn g() {}
+ pub(in self) fn h() {}
+}
+with_span! {
+ span
+ pub(self) fn i() {}
+ pub(in self) fn j() {}
+}
+
+// not really anything more to test. just a really simple lint overall
diff --git a/src/tools/clippy/tests/ui/pub_without_shorthand.stderr b/src/tools/clippy/tests/ui/pub_without_shorthand.stderr
new file mode 100644
index 000000000..a18c9bf89
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_without_shorthand.stderr
@@ -0,0 +1,22 @@
+error: usage of `pub` with `in`
+ --> $DIR/pub_without_shorthand.rs:14:1
+ |
+LL | pub(in self) fn b() {}
+ | ^^^^^^^^^^^^ help: remove it: `pub(self)`
+ |
+ = note: `-D clippy::pub-without-shorthand` implied by `-D warnings`
+
+error: usage of `pub` with `in`
+ --> $DIR/pub_without_shorthand.rs:18:5
+ |
+LL | pub(in super) fn d() {}
+ | ^^^^^^^^^^^^^ help: remove it: `pub(super)`
+
+error: usage of `pub` with `in`
+ --> $DIR/pub_without_shorthand.rs:22:5
+ |
+LL | pub(in crate) fn m() {}
+ | ^^^^^^^^^^^^^ help: remove it: `pub(crate)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed
index 7f1660f31..2d8920ccc 100644
--- a/src/tools/clippy/tests/ui/question_mark.fixed
+++ b/src/tools/clippy/tests/ui/question_mark.fixed
@@ -1,4 +1,5 @@
//@run-rustfix
+#![feature(try_blocks)]
#![allow(unreachable_code)]
#![allow(dead_code)]
#![allow(clippy::unnecessary_wraps)]
@@ -227,6 +228,27 @@ fn pattern() -> Result<(), PatternedError> {
fn main() {}
+// `?` is not the same as `return None;` if inside of a try block
+fn issue8628(a: Option<u32>) -> Option<u32> {
+ let b: Option<u32> = try {
+ if a.is_none() {
+ return None;
+ }
+ 32
+ };
+ b.or(Some(128))
+}
+
+fn issue6828_nested_body() -> Option<u32> {
+ try {
+ fn f2(a: Option<i32>) -> Option<i32> {
+ a?;
+ Some(32)
+ }
+ 123
+ }
+}
+
// should not lint, `?` operator not available in const context
const fn issue9175(option: Option<()>) -> Option<()> {
if option.is_none() {
diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs
index a90eae50e..69451c17e 100644
--- a/src/tools/clippy/tests/ui/question_mark.rs
+++ b/src/tools/clippy/tests/ui/question_mark.rs
@@ -1,4 +1,5 @@
//@run-rustfix
+#![feature(try_blocks)]
#![allow(unreachable_code)]
#![allow(dead_code)]
#![allow(clippy::unnecessary_wraps)]
@@ -263,6 +264,31 @@ fn pattern() -> Result<(), PatternedError> {
fn main() {}
+// `?` is not the same as `return None;` if inside of a try block
+fn issue8628(a: Option<u32>) -> Option<u32> {
+ let b: Option<u32> = try {
+ if a.is_none() {
+ return None;
+ }
+ 32
+ };
+ b.or(Some(128))
+}
+
+fn issue6828_nested_body() -> Option<u32> {
+ try {
+ fn f2(a: Option<i32>) -> Option<i32> {
+ if a.is_none() {
+ return None;
+ // do lint here, the outer `try` is not relevant here
+ // https://github.com/rust-lang/rust-clippy/pull/11001#issuecomment-1610636867
+ }
+ Some(32)
+ }
+ 123
+ }
+}
+
// should not lint, `?` operator not available in const context
const fn issue9175(option: Option<()>) -> Option<()> {
if option.is_none() {
diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr
index 23172d7e5..2cfd75863 100644
--- a/src/tools/clippy/tests/ui/question_mark.stderr
+++ b/src/tools/clippy/tests/ui/question_mark.stderr
@@ -1,5 +1,5 @@
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:7:5
+ --> $DIR/question_mark.rs:8:5
|
LL | / if a.is_none() {
LL | | return None;
@@ -9,7 +9,7 @@ LL | | }
= note: `-D clippy::question-mark` implied by `-D warnings`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:52:9
+ --> $DIR/question_mark.rs:53:9
|
LL | / if (self.opt).is_none() {
LL | | return None;
@@ -17,7 +17,7 @@ LL | | }
| |_________^ help: replace it with: `(self.opt)?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:56:9
+ --> $DIR/question_mark.rs:57:9
|
LL | / if self.opt.is_none() {
LL | | return None
@@ -25,7 +25,7 @@ LL | | }
| |_________^ help: replace it with: `self.opt?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:60:17
+ --> $DIR/question_mark.rs:61:17
|
LL | let _ = if self.opt.is_none() {
| _________________^
@@ -36,7 +36,7 @@ LL | | };
| |_________^ help: replace it with: `Some(self.opt?)`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:66:17
+ --> $DIR/question_mark.rs:67:17
|
LL | let _ = if let Some(x) = self.opt {
| _________________^
@@ -47,7 +47,7 @@ LL | | };
| |_________^ help: replace it with: `self.opt?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:83:9
+ --> $DIR/question_mark.rs:84:9
|
LL | / if self.opt.is_none() {
LL | | return None;
@@ -55,7 +55,7 @@ LL | | }
| |_________^ help: replace it with: `self.opt.as_ref()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:91:9
+ --> $DIR/question_mark.rs:92:9
|
LL | / if self.opt.is_none() {
LL | | return None;
@@ -63,7 +63,7 @@ LL | | }
| |_________^ help: replace it with: `self.opt.as_ref()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:99:9
+ --> $DIR/question_mark.rs:100:9
|
LL | / if self.opt.is_none() {
LL | | return None;
@@ -71,7 +71,7 @@ LL | | }
| |_________^ help: replace it with: `self.opt.as_ref()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:106:26
+ --> $DIR/question_mark.rs:107:26
|
LL | let v: &Vec<_> = if let Some(ref v) = self.opt {
| __________________________^
@@ -82,7 +82,7 @@ LL | | };
| |_________^ help: replace it with: `self.opt.as_ref()?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:116:17
+ --> $DIR/question_mark.rs:117:17
|
LL | let v = if let Some(v) = self.opt {
| _________________^
@@ -93,7 +93,7 @@ LL | | };
| |_________^ help: replace it with: `self.opt?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:131:5
+ --> $DIR/question_mark.rs:132:5
|
LL | / if f().is_none() {
LL | | return None;
@@ -101,13 +101,13 @@ LL | | }
| |_____^ help: replace it with: `f()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:143:13
+ --> $DIR/question_mark.rs:144:13
|
LL | let _ = if let Ok(x) = x { x } else { return x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:145:5
+ --> $DIR/question_mark.rs:146:5
|
LL | / if x.is_err() {
LL | | return x;
@@ -115,7 +115,7 @@ LL | | }
| |_____^ help: replace it with: `x?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:196:5
+ --> $DIR/question_mark.rs:197:5
|
LL | / if let Err(err) = func_returning_result() {
LL | | return Err(err);
@@ -123,12 +123,22 @@ LL | | }
| |_____^ help: replace it with: `func_returning_result()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:203:5
+ --> $DIR/question_mark.rs:204:5
|
LL | / if let Err(err) = func_returning_result() {
LL | | return Err(err);
LL | | }
| |_____^ help: replace it with: `func_returning_result()?;`
-error: aborting due to 15 previous errors
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:281:13
+ |
+LL | / if a.is_none() {
+LL | | return None;
+LL | | // do lint here, the outer `try` is not relevant here
+LL | | // https://github.com/rust-lang/rust-clippy/pull/11001#issuecomment-1610636867
+LL | | }
+ | |_____________^ help: replace it with: `a?;`
+
+error: aborting due to 16 previous errors
diff --git a/src/tools/clippy/tests/ui/range.rs b/src/tools/clippy/tests/ui/range.rs
index 628282509..46edf0921 100644
--- a/src/tools/clippy/tests/ui/range.rs
+++ b/src/tools/clippy/tests/ui/range.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::useless_vec)]
#[warn(clippy::range_zip_with_len)]
fn main() {
let v1 = vec![1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/range.stderr b/src/tools/clippy/tests/ui/range.stderr
index dcb506137..ac83b67fd 100644
--- a/src/tools/clippy/tests/ui/range.stderr
+++ b/src/tools/clippy/tests/ui/range.stderr
@@ -1,5 +1,5 @@
error: it is more idiomatic to use `v1.iter().enumerate()`
- --> $DIR/range.rs:5:14
+ --> $DIR/range.rs:6:14
|
LL | let _x = v1.iter().zip(0..v1.len());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs
index 384060e6e..53fcbf3c4 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs
@@ -1,4 +1,5 @@
#![warn(clippy::rc_clone_in_vec_init)]
+#![allow(clippy::useless_vec)]
use std::sync::{Arc, Mutex};
fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
index 7814f5b54..a8fd28b84 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
@@ -1,5 +1,5 @@
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/arc.rs:7:13
+ --> $DIR/arc.rs:8:13
|
LL | let v = vec![Arc::new("x".to_string()); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/arc.rs:15:21
+ --> $DIR/arc.rs:16:21
|
LL | let v = vec![Arc::new("x".to_string()); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -46,7 +46,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/arc.rs:21:13
+ --> $DIR/arc.rs:22:13
|
LL | let v = vec![
| _____________^
@@ -76,7 +76,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/arc.rs:30:14
+ --> $DIR/arc.rs:31:14
|
LL | let v1 = vec![
| ______________^
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs
index 0394457fe..88ea39bf9 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs
@@ -1,4 +1,5 @@
#![warn(clippy::rc_clone_in_vec_init)]
+#![allow(clippy::useless_vec)]
use std::rc::Rc;
use std::sync::Mutex;
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
index 80deb7cb9..eab464800 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
@@ -1,5 +1,5 @@
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/rc.rs:8:13
+ --> $DIR/rc.rs:9:13
|
LL | let v = vec![Rc::new("x".to_string()); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/rc.rs:16:21
+ --> $DIR/rc.rs:17:21
|
LL | let v = vec![Rc::new("x".to_string()); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -46,7 +46,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/rc.rs:22:13
+ --> $DIR/rc.rs:23:13
|
LL | let v = vec![
| _____________^
@@ -76,7 +76,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/rc.rs:31:14
+ --> $DIR/rc.rs:32:14
|
LL | let v1 = vec![
| ______________^
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
index 693c9b553..031421650 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
@@ -1,4 +1,5 @@
#![warn(clippy::rc_clone_in_vec_init)]
+#![allow(clippy::useless_vec)]
use std::rc::{Rc, Weak as UnSyncWeak};
use std::sync::{Arc, Mutex, Weak as SyncWeak};
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
index 789e14a30..1f7a849b1 100644
--- a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
@@ -1,5 +1,5 @@
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:8:13
+ --> $DIR/weak.rs:9:13
|
LL | let v = vec![SyncWeak::<u32>::new(); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:9:14
+ --> $DIR/weak.rs:10:14
|
LL | let v2 = vec![UnSyncWeak::<u32>::new(); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -46,7 +46,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:11:13
+ --> $DIR/weak.rs:12:13
|
LL | let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:12:13
+ --> $DIR/weak.rs:13:13
|
LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -92,7 +92,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:20:21
+ --> $DIR/weak.rs:21:21
|
LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -115,7 +115,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:21:22
+ --> $DIR/weak.rs:22:22
|
LL | let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -138,7 +138,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:27:13
+ --> $DIR/weak.rs:28:13
|
LL | let v = vec![
| _____________^
@@ -168,7 +168,7 @@ LL ~ };
|
error: initializing a reference-counted pointer in `vec![elem; len]`
- --> $DIR/weak.rs:36:14
+ --> $DIR/weak.rs:37:14
|
LL | let v1 = vec![
| ______________^
diff --git a/src/tools/clippy/tests/ui/redundant_at_rest_pattern.fixed b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.fixed
new file mode 100644
index 000000000..080cf13b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.fixed
@@ -0,0 +1,27 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(irrefutable_let_patterns, unused)]
+#![warn(clippy::redundant_at_rest_pattern)]
+
+#[macro_use]
+extern crate proc_macros;
+
+fn main() {
+ if let a = [()] {}
+ if let ref a = [()] {}
+ if let mut a = [()] {}
+ if let ref mut a = [()] {}
+ let v = vec![()];
+ if let a = &*v {}
+ let s = &[()];
+ if let a = s {}
+ // Don't lint
+ if let [..] = &*v {}
+ if let [a] = &*v {}
+ if let [()] = &*v {}
+ if let [first, rest @ ..] = &*v {}
+ if let a = [()] {}
+ external! {
+ if let [a @ ..] = [()] {}
+ }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_at_rest_pattern.rs b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.rs
new file mode 100644
index 000000000..a8a802829
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.rs
@@ -0,0 +1,27 @@
+//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(irrefutable_let_patterns, unused)]
+#![warn(clippy::redundant_at_rest_pattern)]
+
+#[macro_use]
+extern crate proc_macros;
+
+fn main() {
+ if let [a @ ..] = [()] {}
+ if let [ref a @ ..] = [()] {}
+ if let [mut a @ ..] = [()] {}
+ if let [ref mut a @ ..] = [()] {}
+ let v = vec![()];
+ if let [a @ ..] = &*v {}
+ let s = &[()];
+ if let [a @ ..] = s {}
+ // Don't lint
+ if let [..] = &*v {}
+ if let [a] = &*v {}
+ if let [()] = &*v {}
+ if let [first, rest @ ..] = &*v {}
+ if let a = [()] {}
+ external! {
+ if let [a @ ..] = [()] {}
+ }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_at_rest_pattern.stderr b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.stderr
new file mode 100644
index 000000000..e2a4d9ffd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_at_rest_pattern.stderr
@@ -0,0 +1,40 @@
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:10:12
+ |
+LL | if let [a @ ..] = [()] {}
+ | ^^^^^^^^ help: this is better represented with just the binding: `a`
+ |
+ = note: `-D clippy::redundant-at-rest-pattern` implied by `-D warnings`
+
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:11:12
+ |
+LL | if let [ref a @ ..] = [()] {}
+ | ^^^^^^^^^^^^ help: this is better represented with just the binding: `ref a`
+
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:12:12
+ |
+LL | if let [mut a @ ..] = [()] {}
+ | ^^^^^^^^^^^^ help: this is better represented with just the binding: `mut a`
+
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:13:12
+ |
+LL | if let [ref mut a @ ..] = [()] {}
+ | ^^^^^^^^^^^^^^^^ help: this is better represented with just the binding: `ref mut a`
+
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:15:12
+ |
+LL | if let [a @ ..] = &*v {}
+ | ^^^^^^^^ help: this is better represented with just the binding: `a`
+
+error: using a rest pattern to bind an entire slice to a local
+ --> $DIR/redundant_at_rest_pattern.rs:17:12
+ |
+LL | if let [a @ ..] = s {}
+ | ^^^^^^^^ help: this is better represented with just the binding: `a`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed
index cb9583aa6..5037c08eb 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.fixed
+++ b/src/tools/clippy/tests/ui/redundant_clone.fixed
@@ -2,7 +2,12 @@
// rustfix-only-machine-applicable
#![feature(lint_reasons)]
#![warn(clippy::redundant_clone)]
-#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)]
+#![allow(
+ clippy::drop_non_drop,
+ clippy::implicit_clone,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
+)]
use std::ffi::OsString;
use std::path::Path;
diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs
index e5aeacbb5..501898bf1 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.rs
+++ b/src/tools/clippy/tests/ui/redundant_clone.rs
@@ -2,7 +2,12 @@
// rustfix-only-machine-applicable
#![feature(lint_reasons)]
#![warn(clippy::redundant_clone)]
-#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)]
+#![allow(
+ clippy::drop_non_drop,
+ clippy::implicit_clone,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_literal_unwrap
+)]
use std::ffi::OsString;
use std::path::Path;
diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr
index bb5c602d6..8660c0e1f 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.stderr
+++ b/src/tools/clippy/tests/ui/redundant_clone.stderr
@@ -1,180 +1,180 @@
error: redundant clone
- --> $DIR/redundant_clone.rs:11:42
+ --> $DIR/redundant_clone.rs:16:42
|
LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:11:14
+ --> $DIR/redundant_clone.rs:16:14
|
LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::redundant-clone` implied by `-D warnings`
error: redundant clone
- --> $DIR/redundant_clone.rs:14:15
+ --> $DIR/redundant_clone.rs:19:15
|
LL | let _s = s.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:14:14
+ --> $DIR/redundant_clone.rs:19:14
|
LL | let _s = s.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:17:15
+ --> $DIR/redundant_clone.rs:22:15
|
LL | let _s = s.to_string();
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:17:14
+ --> $DIR/redundant_clone.rs:22:14
|
LL | let _s = s.to_string();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:20:15
+ --> $DIR/redundant_clone.rs:25:15
|
LL | let _s = s.to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:20:14
+ --> $DIR/redundant_clone.rs:25:14
|
LL | let _s = s.to_owned();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:22:42
+ --> $DIR/redundant_clone.rs:27:42
|
LL | let _s = Path::new("/a/b/").join("c").to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:22:14
+ --> $DIR/redundant_clone.rs:27:14
|
LL | let _s = Path::new("/a/b/").join("c").to_owned();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:24:42
+ --> $DIR/redundant_clone.rs:29:42
|
LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:24:14
+ --> $DIR/redundant_clone.rs:29:14
|
LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:26:29
+ --> $DIR/redundant_clone.rs:31:29
|
LL | let _s = OsString::new().to_owned();
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:26:14
+ --> $DIR/redundant_clone.rs:31:14
|
LL | let _s = OsString::new().to_owned();
| ^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:28:29
+ --> $DIR/redundant_clone.rs:33:29
|
LL | let _s = OsString::new().to_os_string();
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:28:14
+ --> $DIR/redundant_clone.rs:33:14
|
LL | let _s = OsString::new().to_os_string();
| ^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:39:19
+ --> $DIR/redundant_clone.rs:44:19
|
LL | let _t = tup.0.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:39:14
+ --> $DIR/redundant_clone.rs:44:14
|
LL | let _t = tup.0.clone();
| ^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:71:25
+ --> $DIR/redundant_clone.rs:76:25
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:71:24
+ --> $DIR/redundant_clone.rs:76:24
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:128:15
+ --> $DIR/redundant_clone.rs:133:15
|
LL | let _s = s.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:128:14
+ --> $DIR/redundant_clone.rs:133:14
|
LL | let _s = s.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:129:15
+ --> $DIR/redundant_clone.rs:134:15
|
LL | let _t = t.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:129:14
+ --> $DIR/redundant_clone.rs:134:14
|
LL | let _t = t.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:139:19
+ --> $DIR/redundant_clone.rs:144:19
|
LL | let _f = f.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:139:18
+ --> $DIR/redundant_clone.rs:144:18
|
LL | let _f = f.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:151:14
+ --> $DIR/redundant_clone.rs:156:14
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^ help: remove this
|
note: cloned value is neither consumed nor mutated
- --> $DIR/redundant_clone.rs:151:13
+ --> $DIR/redundant_clone.rs:156:13
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:205:11
+ --> $DIR/redundant_clone.rs:210:11
|
LL | foo(&x.clone(), move || {
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:205:10
+ --> $DIR/redundant_clone.rs:210:10
|
LL | foo(&x.clone(), move || {
| ^
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index 61aed2733..f3669a669 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -3,6 +3,7 @@
#![feature(async_closure)]
#![warn(clippy::redundant_closure_call)]
#![allow(clippy::redundant_async_block)]
+#![allow(clippy::type_complexity)]
#![allow(unused)]
async fn something() -> u32 {
@@ -38,4 +39,50 @@ fn main() {
};
}
m2!();
+ issue9956();
+}
+
+fn issue9956() {
+ assert_eq!(43, 42);
+
+ // ... and some more interesting cases I've found while implementing the fix
+
+ // not actually immediately calling the closure:
+ let a = (|| 42);
+ dbg!(a());
+
+ // immediately calling it inside of a macro
+ dbg!(42);
+
+ // immediately calling only one closure, so we can't remove the other ones
+ let a = (|| || 123);
+ dbg!(a()());
+
+ // nested async closures
+ let a = async { 1 };
+ let h = async { a.await };
+
+ // macro expansion tests
+ macro_rules! echo {
+ ($e:expr) => {
+ $e
+ };
+ }
+ let a = 1;
+ assert_eq!(a, 1);
+ let a = 123;
+ assert_eq!(a, 123);
+
+ // chaining calls, but not closures
+ fn x() -> fn() -> fn() -> fn() -> i32 {
+ || || || 42
+ }
+ let _ = x()()()();
+
+ fn bar() -> fn(i32, i32) {
+ foo
+ }
+ fn foo(_: i32, _: i32) {}
+ bar()(42, 5);
+ foo(42, 5);
}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 56b286635..db8c7f80d 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -3,6 +3,7 @@
#![feature(async_closure)]
#![warn(clippy::redundant_closure_call)]
#![allow(clippy::redundant_async_block)]
+#![allow(clippy::type_complexity)]
#![allow(unused)]
async fn something() -> u32 {
@@ -38,4 +39,50 @@ fn main() {
};
}
m2!();
+ issue9956();
+}
+
+fn issue9956() {
+ assert_eq!((|| || 43)()(), 42);
+
+ // ... and some more interesting cases I've found while implementing the fix
+
+ // not actually immediately calling the closure:
+ let a = (|| 42);
+ dbg!(a());
+
+ // immediately calling it inside of a macro
+ dbg!((|| 42)());
+
+ // immediately calling only one closure, so we can't remove the other ones
+ let a = (|| || || 123)();
+ dbg!(a()());
+
+ // nested async closures
+ let a = (|| || || || async || 1)()()()()();
+ let h = async { a.await };
+
+ // macro expansion tests
+ macro_rules! echo {
+ ($e:expr) => {
+ $e
+ };
+ }
+ let a = (|| echo!(|| echo!(|| 1)))()()();
+ assert_eq!(a, 1);
+ let a = (|| echo!((|| 123)))()();
+ assert_eq!(a, 123);
+
+ // chaining calls, but not closures
+ fn x() -> fn() -> fn() -> fn() -> i32 {
+ || || || 42
+ }
+ let _ = x()()()();
+
+ fn bar() -> fn(i32, i32) {
+ foo
+ }
+ fn foo(_: i32, _: i32) {}
+ bar()((|| || 42)()(), 5);
+ foo((|| || 42)()(), 5);
}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index 8a1f07716..618f5e071 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -1,5 +1,5 @@
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:17:13
+ --> $DIR/redundant_closure_call_fixable.rs:18:13
|
LL | let a = (|| 42)();
| ^^^^^^^^^ help: try doing something like: `42`
@@ -7,7 +7,7 @@ LL | let a = (|| 42)();
= note: `-D clippy::redundant-closure-call` implied by `-D warnings`
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:18:13
+ --> $DIR/redundant_closure_call_fixable.rs:19:13
|
LL | let b = (async || {
| _____________^
@@ -27,7 +27,7 @@ LL ~ };
|
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:23:13
+ --> $DIR/redundant_closure_call_fixable.rs:24:13
|
LL | let c = (|| {
| _____________^
@@ -47,13 +47,13 @@ LL ~ };
|
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:28:13
+ --> $DIR/redundant_closure_call_fixable.rs:29:13
|
LL | let d = (async || something().await)();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:37:13
+ --> $DIR/redundant_closure_call_fixable.rs:38:13
|
LL | (|| m!())()
| ^^^^^^^^^^^ help: try doing something like: `m!()`
@@ -64,7 +64,7 @@ LL | m2!();
= note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:32:13
+ --> $DIR/redundant_closure_call_fixable.rs:33:13
|
LL | (|| 0)()
| ^^^^^^^^ help: try doing something like: `0`
@@ -74,5 +74,53 @@ LL | m2!();
|
= note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 6 previous errors
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:46:16
+ |
+LL | assert_eq!((|| || 43)()(), 42);
+ | ^^^^^^^^^^^^^^ help: try doing something like: `43`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:55:10
+ |
+LL | dbg!((|| 42)());
+ | ^^^^^^^^^ help: try doing something like: `42`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:58:13
+ |
+LL | let a = (|| || || 123)();
+ | ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:62:13
+ |
+LL | let a = (|| || || || async || 1)()()()()();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { 1 }`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:71:13
+ |
+LL | let a = (|| echo!(|| echo!(|| 1)))()()();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `1`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:73:13
+ |
+LL | let a = (|| echo!((|| 123)))()();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `123`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:86:11
+ |
+LL | bar()((|| || 42)()(), 5);
+ | ^^^^^^^^^^^^^^ help: try doing something like: `42`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:87:9
+ |
+LL | foo((|| || 42)()(), 5);
+ | ^^^^^^^^^^^^^^ help: try doing something like: `42`
+
+error: aborting due to 14 previous errors
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
index bebdf8971..d1134de5a 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
@@ -2,7 +2,12 @@
// Issue #5746
#![warn(clippy::redundant_pattern_matching)]
-#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::equatable_if_let,
+ clippy::needless_if,
+ clippy::needless_else
+)]
use std::task::Poll::{Pending, Ready};
fn main() {
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
index 8fb6ed5f7..d144086e7 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
@@ -2,7 +2,12 @@
// Issue #5746
#![warn(clippy::redundant_pattern_matching)]
-#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::equatable_if_let,
+ clippy::needless_if,
+ clippy::needless_else
+)]
use std::task::Poll::{Pending, Ready};
fn main() {
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
index 23f08103f..e9ea3f2e6 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:12:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:17:12
|
LL | if let Ok(_) = m.lock() {}
| -------^^^^^----------- help: try this: `if m.lock().is_ok()`
@@ -9,7 +9,7 @@ LL | if let Ok(_) = m.lock() {}
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:13:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
|
LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
| -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()`
@@ -18,7 +18,7 @@ LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:16:16
+ --> $DIR/redundant_pattern_matching_drop_order.rs:21:16
|
LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
| -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
@@ -27,7 +27,7 @@ LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:23:12
|
LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
| -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
@@ -36,31 +36,31 @@ LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:21:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:26:12
|
LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
| -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:22:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:27:12
|
LL | if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
| -------^^^^^^------------------------------------------ help: try this: `if Err::<std::sync::MutexGuard<()>, _>(()).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:24:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
|
LL | if let Ok(_) = Ok::<_, ()>(String::new()) {}
| -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:25:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:30:12
|
LL | if let Err(_) = Err::<(), _>((String::new(), ())) {}
| -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:28:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:33:12
|
LL | if let Some(_) = Some(m.lock()) {}
| -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()`
@@ -69,7 +69,7 @@ LL | if let Some(_) = Some(m.lock()) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
|
LL | if let Some(_) = Some(m.lock().unwrap().0) {}
| -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()`
@@ -78,7 +78,7 @@ LL | if let Some(_) = Some(m.lock().unwrap().0) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:32:16
+ --> $DIR/redundant_pattern_matching_drop_order.rs:37:16
|
LL | if let None = None::<std::sync::MutexGuard<()>> {}
| -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
@@ -87,7 +87,7 @@ LL | if let None = None::<std::sync::MutexGuard<()>> {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:39:12
|
LL | if let None = None::<std::sync::MutexGuard<()>> {
| -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
@@ -96,25 +96,25 @@ LL | if let None = None::<std::sync::MutexGuard<()>> {
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:38:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:43:12
|
LL | if let None = None::<std::sync::MutexGuard<()>> {}
| -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:40:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
|
LL | if let Some(_) = Some(String::new()) {}
| -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:41:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:46:12
|
LL | if let Some(_) = Some((String::new(), ())) {}
| -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:44:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:49:12
|
LL | if let Ready(_) = Ready(m.lock()) {}
| -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()`
@@ -123,7 +123,7 @@ LL | if let Ready(_) = Ready(m.lock()) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
|
LL | if let Ready(_) = Ready(m.lock().unwrap().0) {}
| -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()`
@@ -132,7 +132,7 @@ LL | if let Ready(_) = Ready(m.lock().unwrap().0) {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:48:16
+ --> $DIR/redundant_pattern_matching_drop_order.rs:53:16
|
LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
| -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
@@ -141,7 +141,7 @@ LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:55:12
|
LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {
| -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
@@ -150,19 +150,19 @@ LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:54:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:59:12
|
LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
| -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:56:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:61:12
|
LL | if let Ready(_) = Ready(String::new()) {}
| -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_drop_order.rs:57:12
+ --> $DIR/redundant_pattern_matching_drop_order.rs:62:12
|
LL | if let Ready(_) = Ready((String::new(), ())) {}
| -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()`
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed
index a9faf12cd..75ed14344 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed
@@ -4,6 +4,7 @@
#![allow(
clippy::match_like_matches_macro,
clippy::needless_bool,
+ clippy::needless_if,
clippy::uninlined_format_args
)]
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs
index 574671d03..9ac77409f 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs
@@ -4,6 +4,7 @@
#![allow(
clippy::match_like_matches_macro,
clippy::needless_bool,
+ clippy::needless_if,
clippy::uninlined_format_args
)]
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr
index 536b589de..6d1fb2964 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:17:12
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12
|
LL | if let V4(_) = &ipaddr {}
| -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()`
@@ -7,31 +7,31 @@ LL | if let V4(_) = &ipaddr {}
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:19:12
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:20:12
|
LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
| -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:21:12
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:22:12
|
LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
| -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:23:15
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:24:15
|
LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
| ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:25:15
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:26:15
|
LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
| ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:35:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:36:5
|
LL | / match V4(Ipv4Addr::LOCALHOST) {
LL | | V4(_) => true,
@@ -40,7 +40,7 @@ LL | | };
| |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:40:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:41:5
|
LL | / match V4(Ipv4Addr::LOCALHOST) {
LL | | V4(_) => false,
@@ -49,7 +49,7 @@ LL | | };
| |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:45:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:46:5
|
LL | / match V6(Ipv6Addr::LOCALHOST) {
LL | | V4(_) => false,
@@ -58,7 +58,7 @@ LL | | };
| |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:50:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:51:5
|
LL | / match V6(Ipv6Addr::LOCALHOST) {
LL | | V4(_) => true,
@@ -67,49 +67,49 @@ LL | | };
| |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:55:20
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:56:20
|
LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) {
| -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:63:20
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:64:20
|
LL | let _ = if let V4(_) = gen_ipaddr() {
| -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:65:19
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:66:19
|
LL | } else if let V6(_) = gen_ipaddr() {
| -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:77:12
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:78:12
|
LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
| -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:79:12
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:80:12
|
LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
| -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:81:15
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:82:15
|
LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
| ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:83:15
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:84:15
|
LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
| ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()`
error: redundant pattern matching, consider using `is_ipv4()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:85:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:86:5
|
LL | / match V4(Ipv4Addr::LOCALHOST) {
LL | | V4(_) => true,
@@ -118,7 +118,7 @@ LL | | };
| |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()`
error: redundant pattern matching, consider using `is_ipv6()`
- --> $DIR/redundant_pattern_matching_ipaddr.rs:90:5
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:91:5
|
LL | / match V6(Ipv6Addr::LOCALHOST) {
LL | | V4(_) => false,
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 accdf1da9..a63ba5809 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
@@ -5,6 +5,7 @@
#![allow(
unused_must_use,
clippy::needless_bool,
+ clippy::needless_if,
clippy::match_like_matches_macro,
clippy::equatable_if_let,
clippy::if_same_then_else
@@ -47,6 +48,7 @@ fn main() {
issue6067();
issue10726();
+ issue10803();
let _ = if gen_opt().is_some() {
1
@@ -107,3 +109,14 @@ fn issue10726() {
_ => false,
};
}
+
+fn issue10803() {
+ let x = Some(42);
+
+ let _ = x.is_some();
+
+ let _ = x.is_none();
+
+ // Don't lint
+ let _ = matches!(x, Some(16));
+}
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 ec684bdf7..631f90916 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
@@ -5,6 +5,7 @@
#![allow(
unused_must_use,
clippy::needless_bool,
+ clippy::needless_if,
clippy::match_like_matches_macro,
clippy::equatable_if_let,
clippy::if_same_then_else
@@ -56,6 +57,7 @@ fn main() {
issue6067();
issue10726();
+ issue10803();
let _ = if let Some(_) = gen_opt() {
1
@@ -134,3 +136,14 @@ fn issue10726() {
_ => false,
};
}
+
+fn issue10803() {
+ let x = Some(42);
+
+ let _ = matches!(x, Some(_));
+
+ let _ = matches!(x, None);
+
+ // Don't lint
+ let _ = matches!(x, Some(16));
+}
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 a69eb3905..717b603c4 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:14:12
+ --> $DIR/redundant_pattern_matching_option.rs:15:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
@@ -7,43 +7,43 @@ LL | if let None = None::<()> {}
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:16:12
+ --> $DIR/redundant_pattern_matching_option.rs:17:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:18:12
+ --> $DIR/redundant_pattern_matching_option.rs:19:12
|
LL | if let Some(_) = Some(42) {
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:24:15
+ --> $DIR/redundant_pattern_matching_option.rs:25:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:26:15
+ --> $DIR/redundant_pattern_matching_option.rs:27:15
|
LL | while let None = Some(42) {}
| ----------^^^^----------- help: try this: `while Some(42).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:28:15
+ --> $DIR/redundant_pattern_matching_option.rs:29:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:31:15
+ --> $DIR/redundant_pattern_matching_option.rs:32:15
|
LL | while let Some(_) = v.pop() {
| ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:39:5
+ --> $DIR/redundant_pattern_matching_option.rs:40:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
@@ -52,7 +52,7 @@ LL | | };
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:44:5
+ --> $DIR/redundant_pattern_matching_option.rs:45:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
@@ -61,7 +61,7 @@ LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:49:13
+ --> $DIR/redundant_pattern_matching_option.rs:50:13
|
LL | let _ = match None::<()> {
| _____________^
@@ -71,55 +71,55 @@ LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:55:20
+ --> $DIR/redundant_pattern_matching_option.rs:56:20
|
LL | let _ = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try this: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:60:20
+ --> $DIR/redundant_pattern_matching_option.rs:62:20
|
LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:62:19
+ --> $DIR/redundant_pattern_matching_option.rs:64:19
|
LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try this: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:68:12
+ --> $DIR/redundant_pattern_matching_option.rs:70:12
|
LL | if let Some(..) = gen_opt() {}
| -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:83:12
+ --> $DIR/redundant_pattern_matching_option.rs:85:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:85:12
+ --> $DIR/redundant_pattern_matching_option.rs:87:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:87:15
+ --> $DIR/redundant_pattern_matching_option.rs:89:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:89:15
+ --> $DIR/redundant_pattern_matching_option.rs:91:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:91:5
+ --> $DIR/redundant_pattern_matching_option.rs:93:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
@@ -128,7 +128,7 @@ LL | | };
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:96:5
+ --> $DIR/redundant_pattern_matching_option.rs:98:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
@@ -137,19 +137,19 @@ LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:104:12
+ --> $DIR/redundant_pattern_matching_option.rs:106:12
|
LL | if let None = *(&None::<()>) {}
| -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:105:12
+ --> $DIR/redundant_pattern_matching_option.rs:107:12
|
LL | if let None = *&None::<()> {}
| -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:111:5
+ --> $DIR/redundant_pattern_matching_option.rs:113:5
|
LL | / match x {
LL | | Some(_) => true,
@@ -158,7 +158,7 @@ LL | | };
| |_____^ help: try this: `x.is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:116:5
+ --> $DIR/redundant_pattern_matching_option.rs:118:5
|
LL | / match x {
LL | | None => true,
@@ -167,7 +167,7 @@ LL | | };
| |_____^ help: try this: `x.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:121:5
+ --> $DIR/redundant_pattern_matching_option.rs:123:5
|
LL | / match x {
LL | | Some(_) => false,
@@ -176,7 +176,7 @@ LL | | };
| |_____^ help: try this: `x.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:126:5
+ --> $DIR/redundant_pattern_matching_option.rs:128:5
|
LL | / match x {
LL | | None => false,
@@ -184,5 +184,17 @@ LL | | _ => true,
LL | | };
| |_____^ help: try this: `x.is_some()`
-error: aborting due to 26 previous errors
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:143:13
+ |
+LL | let _ = matches!(x, Some(_));
+ | ^^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:145:13
+ |
+LL | let _ = matches!(x, None);
+ | ^^^^^^^^^^^^^^^^^ help: try this: `x.is_none()`
+
+error: aborting due to 28 previous errors
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
index bf3e69220..f739deaf5 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
@@ -5,6 +5,7 @@
#![allow(
unused_must_use,
clippy::needless_bool,
+ clippy::needless_if,
clippy::match_like_matches_macro,
clippy::equatable_if_let,
clippy::if_same_then_else
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
index 892a21d9d..88dde02b3 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
@@ -5,6 +5,7 @@
#![allow(
unused_must_use,
clippy::needless_bool,
+ clippy::needless_if,
clippy::match_like_matches_macro,
clippy::equatable_if_let,
clippy::if_same_then_else
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
index 1b480f315..b89fde35f 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:16:12
+ --> $DIR/redundant_pattern_matching_poll.rs:17:12
|
LL | if let Pending = Pending::<()> {}
| -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
@@ -7,37 +7,37 @@ LL | if let Pending = Pending::<()> {}
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:18:12
+ --> $DIR/redundant_pattern_matching_poll.rs:19:12
|
LL | if let Ready(_) = Ready(42) {}
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:20:12
+ --> $DIR/redundant_pattern_matching_poll.rs:21:12
|
LL | if let Ready(_) = Ready(42) {
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:26:15
+ --> $DIR/redundant_pattern_matching_poll.rs:27:15
|
LL | while let Ready(_) = Ready(42) {}
| ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:28:15
+ --> $DIR/redundant_pattern_matching_poll.rs:29:15
|
LL | while let Pending = Ready(42) {}
| ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:30:15
+ --> $DIR/redundant_pattern_matching_poll.rs:31:15
|
LL | while let Pending = Pending::<()> {}
| ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:36:5
+ --> $DIR/redundant_pattern_matching_poll.rs:37:5
|
LL | / match Ready(42) {
LL | | Ready(_) => true,
@@ -46,7 +46,7 @@ LL | | };
| |_____^ help: try this: `Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:41:5
+ --> $DIR/redundant_pattern_matching_poll.rs:42:5
|
LL | / match Pending::<()> {
LL | | Ready(_) => false,
@@ -55,7 +55,7 @@ LL | | };
| |_____^ help: try this: `Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:46:13
+ --> $DIR/redundant_pattern_matching_poll.rs:47:13
|
LL | let _ = match Pending::<()> {
| _____________^
@@ -65,49 +65,49 @@ LL | | };
| |_____^ help: try this: `Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:52:20
+ --> $DIR/redundant_pattern_matching_poll.rs:53:20
|
LL | let _ = if let Ready(_) = poll { true } else { false };
| -------^^^^^^^^------- help: try this: `if poll.is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:56:20
+ --> $DIR/redundant_pattern_matching_poll.rs:57:20
|
LL | let _ = if let Ready(_) = gen_poll() {
| -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:58:19
+ --> $DIR/redundant_pattern_matching_poll.rs:59:19
|
LL | } else if let Pending = gen_poll() {
| -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:74:12
+ --> $DIR/redundant_pattern_matching_poll.rs:75:12
|
LL | if let Ready(_) = Ready(42) {}
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:76:12
+ --> $DIR/redundant_pattern_matching_poll.rs:77:12
|
LL | if let Pending = Pending::<()> {}
| -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:78:15
+ --> $DIR/redundant_pattern_matching_poll.rs:79:15
|
LL | while let Ready(_) = Ready(42) {}
| ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:80:15
+ --> $DIR/redundant_pattern_matching_poll.rs:81:15
|
LL | while let Pending = Pending::<()> {}
| ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:82:5
+ --> $DIR/redundant_pattern_matching_poll.rs:83:5
|
LL | / match Ready(42) {
LL | | Ready(_) => true,
@@ -116,7 +116,7 @@ LL | | };
| |_____^ help: try this: `Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:87:5
+ --> $DIR/redundant_pattern_matching_poll.rs:88:5
|
LL | / match Pending::<()> {
LL | | Ready(_) => false,
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
index e4032ae44..343e0d043 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
@@ -6,6 +6,7 @@
clippy::if_same_then_else,
clippy::match_like_matches_macro,
clippy::needless_bool,
+ clippy::needless_if,
clippy::uninlined_format_args,
clippy::unnecessary_wraps
)]
@@ -44,6 +45,7 @@ fn main() {
issue6067();
issue6065();
issue10726();
+ issue10803();
let _ = if gen_res().is_ok() {
1
@@ -133,3 +135,17 @@ fn issue10726() {
_ => true,
};
}
+
+fn issue10803() {
+ let x: Result<i32, i32> = Ok(42);
+
+ let _ = x.is_ok();
+
+ let _ = x.is_err();
+
+ // Don't lint
+ let _ = matches!(x, Ok(16));
+
+ // Don't lint
+ let _ = matches!(x, Err(16));
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
index 39eb10df8..4d64eafe5 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
@@ -6,6 +6,7 @@
clippy::if_same_then_else,
clippy::match_like_matches_macro,
clippy::needless_bool,
+ clippy::needless_if,
clippy::uninlined_format_args,
clippy::unnecessary_wraps
)]
@@ -56,6 +57,7 @@ fn main() {
issue6067();
issue6065();
issue10726();
+ issue10803();
let _ = if let Ok(_) = gen_res() {
1
@@ -163,3 +165,17 @@ fn issue10726() {
_ => true,
};
}
+
+fn issue10803() {
+ let x: Result<i32, i32> = Ok(42);
+
+ let _ = matches!(x, Ok(_));
+
+ let _ = matches!(x, Err(_));
+
+ // Don't lint
+ let _ = matches!(x, Ok(16));
+
+ // Don't lint
+ let _ = matches!(x, Err(16));
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
index 5893ae4dc..f6ce666bb 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
@@ -1,5 +1,5 @@
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:15:12
+ --> $DIR/redundant_pattern_matching_result.rs:16:12
|
LL | if let Ok(_) = &result {}
| -------^^^^^---------- help: try this: `if result.is_ok()`
@@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {}
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:17:12
+ --> $DIR/redundant_pattern_matching_result.rs:18:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:19:12
+ --> $DIR/redundant_pattern_matching_result.rs:20:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:21:15
+ --> $DIR/redundant_pattern_matching_result.rs:22:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:23:15
+ --> $DIR/redundant_pattern_matching_result.rs:24:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:33:5
+ --> $DIR/redundant_pattern_matching_result.rs:34:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
@@ -40,7 +40,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:38:5
+ --> $DIR/redundant_pattern_matching_result.rs:39:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => false,
@@ -49,7 +49,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:43:5
+ --> $DIR/redundant_pattern_matching_result.rs:44:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
@@ -58,7 +58,7 @@ LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:48:5
+ --> $DIR/redundant_pattern_matching_result.rs:49:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => true,
@@ -67,73 +67,73 @@ LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:53:20
+ --> $DIR/redundant_pattern_matching_result.rs:54:20
|
LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
| -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:60:20
+ --> $DIR/redundant_pattern_matching_result.rs:62:20
|
LL | let _ = if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:62:19
+ --> $DIR/redundant_pattern_matching_result.rs:64:19
|
LL | } else if let Err(_) = gen_res() {
| -------^^^^^^------------ help: try this: `if gen_res().is_err()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:85:19
+ --> $DIR/redundant_pattern_matching_result.rs:87:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:86:16
+ --> $DIR/redundant_pattern_matching_result.rs:88:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:92:12
+ --> $DIR/redundant_pattern_matching_result.rs:94:12
|
LL | if let Some(_) = m!() {}
| -------^^^^^^^------- help: try this: `if m!().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:93:15
+ --> $DIR/redundant_pattern_matching_result.rs:95:15
|
LL | while let Some(_) = m!() {}
| ----------^^^^^^^------- help: try this: `while m!().is_some()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:111:12
+ --> $DIR/redundant_pattern_matching_result.rs:113:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:113:12
+ --> $DIR/redundant_pattern_matching_result.rs:115:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:115:15
+ --> $DIR/redundant_pattern_matching_result.rs:117:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:117:15
+ --> $DIR/redundant_pattern_matching_result.rs:119:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:119:5
+ --> $DIR/redundant_pattern_matching_result.rs:121:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
@@ -142,7 +142,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:124:5
+ --> $DIR/redundant_pattern_matching_result.rs:126:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
@@ -151,7 +151,7 @@ LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:134:5
+ --> $DIR/redundant_pattern_matching_result.rs:136:5
|
LL | / match x {
LL | | Ok(_) => true,
@@ -160,7 +160,7 @@ LL | | };
| |_____^ help: try this: `x.is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:139:5
+ --> $DIR/redundant_pattern_matching_result.rs:141:5
|
LL | / match x {
LL | | Ok(_) => false,
@@ -169,7 +169,7 @@ LL | | };
| |_____^ help: try this: `x.is_err()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:144:5
+ --> $DIR/redundant_pattern_matching_result.rs:146:5
|
LL | / match x {
LL | | Err(_) => true,
@@ -178,7 +178,7 @@ LL | | };
| |_____^ help: try this: `x.is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:149:5
+ --> $DIR/redundant_pattern_matching_result.rs:151:5
|
LL | / match x {
LL | | Err(_) => false,
@@ -186,5 +186,17 @@ LL | | _ => true,
LL | | };
| |_____^ help: try this: `x.is_ok()`
-error: aborting due to 26 previous errors
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:172:13
+ |
+LL | let _ = matches!(x, Ok(_));
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `x.is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:174:13
+ |
+LL | let _ = matches!(x, Err(_));
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_err()`
+
+error: aborting due to 28 previous errors
diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed
index f65c0fdd3..a1ed491bb 100644
--- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed
@@ -14,7 +14,7 @@ mod m1 {
}
pub mod m1_2 {
- // ^ private due to m1
+ //:^ private due to m1
fn f() {}
pub fn g() {} // private due to m1_2 and m1
pub fn h() {}
@@ -39,7 +39,7 @@ pub(crate) mod m2 {
}
pub mod m2_2 {
- // ^ already crate visible due to m2
+ //:^ already crate visible due to m2
fn f() {}
pub fn g() {} // already crate visible due to m2_2 and m2
pub fn h() {}
@@ -64,7 +64,7 @@ pub mod m3 {
}
pub(crate) mod m3_2 {
- // ^ ok
+ //:^ ok
fn f() {}
pub fn g() {} // already crate visible due to m3_2
pub fn h() {}
@@ -89,7 +89,7 @@ mod m4 {
}
pub mod m4_2 {
- // ^ private: not re-exported by `pub use m4::*`
+ //:^ private: not re-exported by `pub use m4::*`
fn f() {}
pub fn g() {} // private due to m4_2
pub fn h() {}
diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs
index fb07fed98..9accd297f 100644
--- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs
+++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs
@@ -14,7 +14,7 @@ mod m1 {
}
pub(crate) mod m1_2 {
- // ^ private due to m1
+ //:^ private due to m1
fn f() {}
pub(crate) fn g() {} // private due to m1_2 and m1
pub fn h() {}
@@ -39,7 +39,7 @@ pub(crate) mod m2 {
}
pub(crate) mod m2_2 {
- // ^ already crate visible due to m2
+ //:^ already crate visible due to m2
fn f() {}
pub(crate) fn g() {} // already crate visible due to m2_2 and m2
pub fn h() {}
@@ -64,7 +64,7 @@ pub mod m3 {
}
pub(crate) mod m3_2 {
- // ^ ok
+ //:^ ok
fn f() {}
pub(crate) fn g() {} // already crate visible due to m3_2
pub fn h() {}
@@ -89,7 +89,7 @@ mod m4 {
}
pub(crate) mod m4_2 {
- // ^ private: not re-exported by `pub use m4::*`
+ //:^ private: not re-exported by `pub use m4::*`
fn f() {}
pub(crate) fn g() {} // private due to m4_2
pub fn h() {}
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
index 2651735d1..a83699ec6 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
@@ -5,39 +5,39 @@
#[derive(Debug)]
struct Foo;
-const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
+const VAR_ONE: &str = "Test constant #1"; // ERROR: Consider removing 'static.
const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
-const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
+const VAR_THREE: &[&str] = &["one", "two"]; // ERROR: Consider removing 'static
-const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
const VAR_SIX: &u8 = &5;
const VAR_HEIGHT: &Foo = &Foo {};
-const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR: Consider removing 'static.
-const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
-const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
+const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR: Consider removing 'static.
-static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
+static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR: Consider removing 'static.
static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
-static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
+static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR: Consider removing 'static
static STATIC_VAR_SIX: &u8 = &5;
static STATIC_VAR_HEIGHT: &Foo = &Foo {};
-static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
+static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR: Consider removing 'static.
-static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
-static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
+static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR: Consider removing 'static.
static mut STATIC_MUT_SLICE: &mut [u32] = &mut [0];
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
index 728665289..b165cbaa3 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
@@ -5,39 +5,39 @@
#[derive(Debug)]
struct Foo;
-const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
+const VAR_ONE: &'static str = "Test constant #1"; // ERROR: Consider removing 'static.
const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
-const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static
-const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
const VAR_SIX: &'static u8 = &5;
const VAR_HEIGHT: &'static Foo = &Foo {};
-const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR: Consider removing 'static.
-const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
-const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static.
-static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
+static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR: Consider removing 'static.
static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
-static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static
static STATIC_VAR_SIX: &'static u8 = &5;
static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
-static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
+static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR: Consider removing 'static.
-static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
-static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static.
static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0];
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
index b2cbd2d9d..a13e5eadf 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
@@ -1,7 +1,7 @@
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:8:17
|
-LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
+LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR: Consider removing 'static.
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
@@ -9,19 +9,19 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:12:21
|
-LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:14:32
|
-LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:14:47
|
-LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
@@ -39,31 +39,31 @@ LL | const VAR_HEIGHT: &'static Foo = &Foo {};
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:20:19
|
-LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR: Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:22:19
|
-LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:24:19
|
-LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static.
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:26:25
|
-LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
+LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR: Consider removing 'static.
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:30:29
|
-LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
@@ -81,19 +81,19 @@ LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:36:27
|
-LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
+LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR: Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:38:27
|
-LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:40:27
|
-LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static.
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
error: statics have by default a `'static` lifetime
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
index f57dd58e2..b3f263a7d 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
@@ -1,12 +1,12 @@
// these are rustfixable, but run-rustfix tests cannot handle them
-const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
-static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
-static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
index cc7e55a75..4e7500903 100644
--- a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
@@ -1,7 +1,7 @@
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:3:18
|
-LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
| -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
|
= note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
@@ -9,7 +9,7 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]];
error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:3:30
|
-LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
@@ -27,25 +27,25 @@ LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:7:40
|
-LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:7:55
|
-LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:9:26
|
-LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
| -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:9:38
|
-LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR: Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.rs b/src/tools/clippy/tests/ui/redundant_type_annotations.rs
new file mode 100644
index 000000000..09dbd3c9b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_type_annotations.rs
@@ -0,0 +1,190 @@
+#![allow(unused)]
+#![warn(clippy::redundant_type_annotations)]
+
+#[derive(Debug, Default)]
+struct Cake<T> {
+ _data: T,
+}
+
+fn make_something<T>() -> T {
+ unimplemented!()
+}
+
+fn make_cake<T: Default>() -> Cake<T> {
+ Cake::<T>::default()
+}
+
+fn plus_one<T: std::ops::Add<u8, Output = T>>(val: T) -> T {
+ val + 1
+}
+
+#[derive(Default)]
+struct Slice {
+ inner: u32,
+}
+
+#[derive(Default)]
+struct Pie {
+ inner: u32,
+ inner_struct: Slice,
+}
+
+enum Pizza {
+ One,
+ Two,
+}
+
+fn return_a_string() -> String {
+ String::new()
+}
+
+fn return_a_struct() -> Pie {
+ Pie::default()
+}
+
+fn return_an_enum() -> Pizza {
+ Pizza::One
+}
+
+fn return_an_int() -> u32 {
+ 5
+}
+
+impl Pie {
+ fn return_an_int(&self) -> u32 {
+ self.inner
+ }
+
+ fn return_a_ref(&self) -> &u32 {
+ &self.inner
+ }
+
+ fn return_a_ref_to_struct(&self) -> &Slice {
+ &self.inner_struct
+ }
+
+ fn associated_return_an_int() -> u32 {
+ 5
+ }
+
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn associated_return_a_string() -> String {
+ String::from("")
+ }
+
+ fn test_method_call(&self) {
+ // Everything here should be lint
+
+ let v: u32 = self.return_an_int();
+ let v: &u32 = self.return_a_ref();
+ let v: &Slice = self.return_a_ref_to_struct();
+ }
+}
+
+fn test_generics() {
+ // The type annotation is needed to determine T
+ let _c: Cake<i32> = make_something();
+
+ // The type annotation is needed to determine the topic
+ let _c: Cake<u8> = make_cake();
+
+ // This could be lint, but currently doesn't
+ let _c: Cake<u8> = make_cake::<u8>();
+
+ // This could be lint, but currently doesn't
+ let _c: u8 = make_something::<u8>();
+
+ // This could be lint, but currently doesn't
+ let _c: u8 = plus_one(5_u8);
+
+ // Annotation needed otherwise T is i32
+ let _c: u8 = plus_one(5);
+
+ // This could be lint, but currently doesn't
+ let _return: String = String::from("test");
+}
+
+fn test_non_locals() {
+ // This shouldn't be lint
+ fn _arg(x: u32) -> u32 {
+ x
+ }
+
+ // This could lint, but probably shouldn't
+ let _closure_arg = |x: u32| x;
+}
+
+trait Trait {
+ type AssocTy;
+}
+
+impl Trait for () {
+ type AssocTy = String;
+}
+
+fn test_complex_types<T>() {
+ // Shouldn't be lint, since the literal will be i32 otherwise
+ let _u8: u8 = 128;
+
+ // This could be lint, but currently doesn't
+ let _tuple_i32: (i32, i32) = (12, 13);
+
+ // Shouldn't be lint, since the tuple will be i32 otherwise
+ let _tuple_u32: (u32, u32) = (1, 2);
+
+ // Should be lint, since the type is determined by the init value, but currently doesn't
+ let _tuple_u32: (u32, u32) = (3_u32, 4_u32);
+
+ // This could be lint, but currently doesn't
+ let _array: [i32; 3] = [5, 6, 7];
+
+ // Shouldn't be lint
+ let _array: [u32; 2] = [8, 9];
+
+ let ty_param: T = make_something();
+
+ let assoc_ty: <() as Trait>::AssocTy = String::new();
+}
+
+fn test_functions() {
+ // Everything here should be lint
+
+ let _return: String = return_a_string();
+
+ let _return: Pie = return_a_struct();
+
+ let _return: Pizza = return_an_enum();
+
+ let _return: u32 = return_an_int();
+
+ let _return: String = String::new();
+
+ let new_pie: Pie = Pie::new();
+
+ let _return: u32 = new_pie.return_an_int();
+
+ let _return: u32 = Pie::associated_return_an_int();
+
+ let _return: String = Pie::associated_return_a_string();
+}
+
+fn test_simple_types() {
+ // Everything here should be lint
+
+ let _var: u32 = u32::MAX;
+
+ let _var: u32 = 5_u32;
+
+ let _var: &str = "test";
+
+ let _var: &[u8] = b"test";
+
+ let _var: bool = false;
+}
+
+fn issue11190() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.stderr b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr
new file mode 100644
index 000000000..988ebe637
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr
@@ -0,0 +1,106 @@
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:81:9
+ |
+LL | let v: u32 = self.return_an_int();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::redundant-type-annotations` implied by `-D warnings`
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:82:9
+ |
+LL | let v: &u32 = self.return_a_ref();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:83:9
+ |
+LL | let v: &Slice = self.return_a_ref_to_struct();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:155:5
+ |
+LL | let _return: String = return_a_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:157:5
+ |
+LL | let _return: Pie = return_a_struct();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:159:5
+ |
+LL | let _return: Pizza = return_an_enum();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:161:5
+ |
+LL | let _return: u32 = return_an_int();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:163:5
+ |
+LL | let _return: String = String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:165:5
+ |
+LL | let new_pie: Pie = Pie::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:167:5
+ |
+LL | let _return: u32 = new_pie.return_an_int();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:169:5
+ |
+LL | let _return: u32 = Pie::associated_return_an_int();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:171:5
+ |
+LL | let _return: String = Pie::associated_return_a_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:177:5
+ |
+LL | let _var: u32 = u32::MAX;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:179:5
+ |
+LL | let _var: u32 = 5_u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:181:5
+ |
+LL | let _var: &str = "test";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:183:5
+ |
+LL | let _var: &[u8] = b"test";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant type annotation
+ --> $DIR/redundant_type_annotations.rs:185:5
+ |
+LL | let _var: bool = false;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs
index a5f79b139..89d1d9494 100644
--- a/src/tools/clippy/tests/ui/regex.rs
+++ b/src/tools/clippy/tests/ui/regex.rs
@@ -1,4 +1,9 @@
-#![allow(unused, clippy::needless_borrow)]
+#![allow(
+ unused,
+ clippy::needless_raw_strings,
+ clippy::needless_raw_string_hashes,
+ clippy::needless_borrow
+)]
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
extern crate regex;
@@ -42,6 +47,11 @@ fn syntax_error() {
let escaped_string_span = Regex::new("\\b\\c");
let aux_span = Regex::new("(?ixi)");
+
+ let should_not_lint = Regex::new("(?u).");
+ let should_not_lint = BRegex::new("(?u).");
+ let invalid_utf8_should_not_lint = BRegex::new("(?-u).");
+ let invalid_utf8_should_lint = Regex::new("(?-u).");
}
fn trivial_regex() {
@@ -71,6 +81,8 @@ fn trivial_regex() {
// non-trivial regexes
let non_trivial_dot = Regex::new("a.b");
let non_trivial_dot_builder = RegexBuilder::new("a.b");
+ let non_trivial_dot = Regex::new(".");
+ let non_trivial_dot = BRegex::new(".");
let non_trivial_eq = Regex::new("^foo|bar$");
let non_trivial_starts_with = Regex::new("^foo|bar");
let non_trivial_ends_with = Regex::new("^foo|bar");
diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr
index 6b8a772e7..21f1cb444 100644
--- a/src/tools/clippy/tests/ui/regex.stderr
+++ b/src/tools/clippy/tests/ui/regex.stderr
@@ -1,5 +1,5 @@
error: trivial regex
- --> $DIR/regex.rs:13:45
+ --> $DIR/regex.rs:18:45
|
LL | let pipe_in_wrong_position = Regex::new("|");
| ^^^
@@ -8,7 +8,7 @@ LL | let pipe_in_wrong_position = Regex::new("|");
= note: `-D clippy::trivial-regex` implied by `-D warnings`
error: trivial regex
- --> $DIR/regex.rs:14:60
+ --> $DIR/regex.rs:19:60
|
LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
| ^^^
@@ -16,7 +16,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
= help: the regex is unlikely to be useful as it is
error: regex syntax error: invalid character class range, the start must be <= the end
- --> $DIR/regex.rs:15:42
+ --> $DIR/regex.rs:20:42
|
LL | let wrong_char_ranice = Regex::new("[z-a]");
| ^^^
@@ -24,7 +24,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]");
= note: `-D clippy::invalid-regex` implied by `-D warnings`
error: regex syntax error: invalid character class range, the start must be <= the end
- --> $DIR/regex.rs:16:37
+ --> $DIR/regex.rs:21:37
|
LL | let some_unicode = Regex::new("[é-è]");
| ^^^
@@ -33,13 +33,13 @@ error: regex parse error:
(
^
error: unclosed group
- --> $DIR/regex.rs:18:33
+ --> $DIR/regex.rs:23:33
|
LL | let some_regex = Regex::new(OPENING_PAREN);
| ^^^^^^^^^^^^^
error: trivial regex
- --> $DIR/regex.rs:20:53
+ --> $DIR/regex.rs:25:53
|
LL | let binary_pipe_in_wrong_position = BRegex::new("|");
| ^^^
@@ -50,7 +50,7 @@ error: regex parse error:
(
^
error: unclosed group
- --> $DIR/regex.rs:21:41
+ --> $DIR/regex.rs:26:41
|
LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
| ^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ error: regex parse error:
(
^
error: unclosed group
- --> $DIR/regex.rs:22:56
+ --> $DIR/regex.rs:27:56
|
LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
| ^^^^^^^^^^^^^
@@ -68,7 +68,7 @@ error: regex parse error:
(
^
error: unclosed group
- --> $DIR/regex.rs:34:37
+ --> $DIR/regex.rs:39:37
|
LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
| ^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ error: regex parse error:
(
^
error: unclosed group
- --> $DIR/regex.rs:35:39
+ --> $DIR/regex.rs:40:39
|
LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
| ^^^^^^^^^^^^^
@@ -86,21 +86,27 @@ error: regex parse error:
/b/c
^^
error: unrecognized escape sequence
- --> $DIR/regex.rs:42:42
+ --> $DIR/regex.rs:47:42
|
-LL | let escaped_string_span = Regex::new("/b/c");
+LL | let escaped_string_span = Regex::new("//b//c");
| ^^^^^^^^
|
= help: consider using a raw string literal: `r".."`
error: regex syntax error: duplicate flag
- --> $DIR/regex.rs:44:34
+ --> $DIR/regex.rs:49:34
|
LL | let aux_span = Regex::new("(?ixi)");
| ^ ^
+error: regex syntax error: pattern can match invalid UTF-8
+ --> $DIR/regex.rs:54:53
+ |
+LL | let invalid_utf8_should_lint = Regex::new("(?-u).");
+ | ^
+
error: trivial regex
- --> $DIR/regex.rs:48:33
+ --> $DIR/regex.rs:58:33
|
LL | let trivial_eq = Regex::new("^foobar$");
| ^^^^^^^^^^
@@ -108,7 +114,7 @@ LL | let trivial_eq = Regex::new("^foobar$");
= help: consider using `==` on `str`s
error: trivial regex
- --> $DIR/regex.rs:50:48
+ --> $DIR/regex.rs:60:48
|
LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
| ^^^^^^^^^^
@@ -116,7 +122,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
= help: consider using `==` on `str`s
error: trivial regex
- --> $DIR/regex.rs:52:42
+ --> $DIR/regex.rs:62:42
|
LL | let trivial_starts_with = Regex::new("^foobar");
| ^^^^^^^^^
@@ -124,7 +130,7 @@ LL | let trivial_starts_with = Regex::new("^foobar");
= help: consider using `str::starts_with`
error: trivial regex
- --> $DIR/regex.rs:54:40
+ --> $DIR/regex.rs:64:40
|
LL | let trivial_ends_with = Regex::new("foobar$");
| ^^^^^^^^^
@@ -132,7 +138,7 @@ LL | let trivial_ends_with = Regex::new("foobar$");
= help: consider using `str::ends_with`
error: trivial regex
- --> $DIR/regex.rs:56:39
+ --> $DIR/regex.rs:66:39
|
LL | let trivial_contains = Regex::new("foobar");
| ^^^^^^^^
@@ -140,7 +146,7 @@ LL | let trivial_contains = Regex::new("foobar");
= help: consider using `str::contains`
error: trivial regex
- --> $DIR/regex.rs:58:39
+ --> $DIR/regex.rs:68:39
|
LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
| ^^^^^^^^^^^^^^^^
@@ -148,15 +154,15 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
= help: consider using `str::contains`
error: trivial regex
- --> $DIR/regex.rs:60:40
+ --> $DIR/regex.rs:70:40
|
-LL | let trivial_backslash = Regex::new("a/.b");
+LL | let trivial_backslash = Regex::new("a//.b");
| ^^^^^^^
|
= help: consider using `str::contains`
error: trivial regex
- --> $DIR/regex.rs:63:36
+ --> $DIR/regex.rs:73:36
|
LL | let trivial_empty = Regex::new("");
| ^^
@@ -164,7 +170,7 @@ LL | let trivial_empty = Regex::new("");
= help: the regex is unlikely to be useful as it is
error: trivial regex
- --> $DIR/regex.rs:65:36
+ --> $DIR/regex.rs:75:36
|
LL | let trivial_empty = Regex::new("^");
| ^^^
@@ -172,7 +178,7 @@ LL | let trivial_empty = Regex::new("^");
= help: the regex is unlikely to be useful as it is
error: trivial regex
- --> $DIR/regex.rs:67:36
+ --> $DIR/regex.rs:77:36
|
LL | let trivial_empty = Regex::new("^$");
| ^^^^
@@ -180,12 +186,12 @@ LL | let trivial_empty = Regex::new("^$");
= help: consider using `str::is_empty`
error: trivial regex
- --> $DIR/regex.rs:69:44
+ --> $DIR/regex.rs:79:44
|
LL | let binary_trivial_empty = BRegex::new("^$");
| ^^^^
|
= help: consider using `str::is_empty`
-error: aborting due to 23 previous errors
+error: aborting due to 24 previous errors
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index dfe45dec8..cc2295ea5 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -29,6 +29,8 @@
#![allow(clippy::recursive_format_impl)]
#![allow(clippy::invisible_characters)]
#![allow(suspicious_double_ref_op)]
+#![allow(invalid_nan_comparisons)]
+#![allow(invalid_reference_casting)]
#![allow(drop_bounds)]
#![allow(dropping_copy_types)]
#![allow(dropping_references)]
@@ -38,12 +40,13 @@
#![allow(array_into_iter)]
#![allow(invalid_atomic_ordering)]
#![allow(invalid_value)]
+#![allow(invalid_from_utf8_unchecked)]
#![allow(let_underscore_drop)]
#![allow(enum_intrinsics_non_enums)]
#![allow(non_fmt_panics)]
#![allow(named_arguments_used_positionally)]
-#![allow(suspicious_double_ref_op)]
#![allow(temporary_cstring_as_ptr)]
+#![allow(undropped_manually_drops)]
#![allow(unknown_lints)]
#![allow(unused_labels)]
#![warn(clippy::almost_complete_range)]
@@ -75,7 +78,9 @@
#![warn(clippy::module_name_repetitions)]
#![warn(clippy::recursive_format_impl)]
#![warn(clippy::invisible_characters)]
+#![warn(invalid_reference_casting)]
#![warn(suspicious_double_ref_op)]
+#![warn(invalid_nan_comparisons)]
#![warn(drop_bounds)]
#![warn(dropping_copy_types)]
#![warn(dropping_references)]
@@ -87,11 +92,13 @@
#![warn(array_into_iter)]
#![warn(invalid_atomic_ordering)]
#![warn(invalid_value)]
+#![warn(invalid_from_utf8_unchecked)]
#![warn(let_underscore_drop)]
#![warn(enum_intrinsics_non_enums)]
#![warn(non_fmt_panics)]
#![warn(named_arguments_used_positionally)]
#![warn(temporary_cstring_as_ptr)]
+#![warn(undropped_manually_drops)]
#![warn(unknown_lints)]
#![warn(unused_labels)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index ce8eca5a3..399335aff 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -29,6 +29,8 @@
#![allow(clippy::recursive_format_impl)]
#![allow(clippy::invisible_characters)]
#![allow(suspicious_double_ref_op)]
+#![allow(invalid_nan_comparisons)]
+#![allow(invalid_reference_casting)]
#![allow(drop_bounds)]
#![allow(dropping_copy_types)]
#![allow(dropping_references)]
@@ -38,12 +40,13 @@
#![allow(array_into_iter)]
#![allow(invalid_atomic_ordering)]
#![allow(invalid_value)]
+#![allow(invalid_from_utf8_unchecked)]
#![allow(let_underscore_drop)]
#![allow(enum_intrinsics_non_enums)]
#![allow(non_fmt_panics)]
#![allow(named_arguments_used_positionally)]
-#![allow(suspicious_double_ref_op)]
#![allow(temporary_cstring_as_ptr)]
+#![allow(undropped_manually_drops)]
#![allow(unknown_lints)]
#![allow(unused_labels)]
#![warn(clippy::almost_complete_letter_range)]
@@ -75,7 +78,9 @@
#![warn(clippy::stutter)]
#![warn(clippy::to_string_in_display)]
#![warn(clippy::zero_width_space)]
+#![warn(clippy::cast_ref_to_mut)]
#![warn(clippy::clone_double_ref)]
+#![warn(clippy::cmp_nan)]
#![warn(clippy::drop_bounds)]
#![warn(clippy::drop_copy)]
#![warn(clippy::drop_ref)]
@@ -87,11 +92,13 @@
#![warn(clippy::into_iter_on_array)]
#![warn(clippy::invalid_atomic_ordering)]
#![warn(clippy::invalid_ref)]
+#![warn(clippy::invalid_utf8_in_unchecked)]
#![warn(clippy::let_underscore_drop)]
#![warn(clippy::mem_discriminant_non_enum)]
#![warn(clippy::panic_params)]
#![warn(clippy::positional_named_format_parameters)]
#![warn(clippy::temporary_cstring_as_ptr)]
+#![warn(clippy::undropped_manually_drops)]
#![warn(clippy::unknown_clippy_lints)]
#![warn(clippy::unused_label)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index 3fca60aa2..079371330 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
- --> $DIR/rename.rs:49:9
+ --> $DIR/rename.rs:52:9
|
LL | #![warn(clippy::almost_complete_letter_range)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -7,286 +7,310 @@ LL | #![warn(clippy::almost_complete_letter_range)]
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
- --> $DIR/rename.rs:50:9
+ --> $DIR/rename.rs:53:9
|
LL | #![warn(clippy::blacklisted_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:51:9
+ --> $DIR/rename.rs:54:9
|
LL | #![warn(clippy::block_in_if_condition_expr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:52:9
+ --> $DIR/rename.rs:55:9
|
LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
- --> $DIR/rename.rs:53:9
+ --> $DIR/rename.rs:56:9
|
LL | #![warn(clippy::box_vec)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
- --> $DIR/rename.rs:54:9
+ --> $DIR/rename.rs:57:9
|
LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- --> $DIR/rename.rs:55:9
+ --> $DIR/rename.rs:58:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
- --> $DIR/rename.rs:56:9
+ --> $DIR/rename.rs:59:9
|
LL | #![warn(clippy::derive_hash_xor_eq)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
- --> $DIR/rename.rs:57:9
+ --> $DIR/rename.rs:60:9
|
LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
- --> $DIR/rename.rs:58:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::disallowed_type)]
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
- --> $DIR/rename.rs:59:9
+ --> $DIR/rename.rs:62:9
|
LL | #![warn(clippy::eval_order_dependence)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
- --> $DIR/rename.rs:60:9
+ --> $DIR/rename.rs:63:9
|
LL | #![warn(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
- --> $DIR/rename.rs:61:9
+ --> $DIR/rename.rs:64:9
|
LL | #![warn(clippy::if_let_some_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
- --> $DIR/rename.rs:62:9
+ --> $DIR/rename.rs:65:9
|
LL | #![warn(clippy::integer_arithmetic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
- --> $DIR/rename.rs:63:9
+ --> $DIR/rename.rs:66:9
|
LL | #![warn(clippy::logic_bug)]
| ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
- --> $DIR/rename.rs:64:9
+ --> $DIR/rename.rs:67:9
|
LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
- --> $DIR/rename.rs:65:9
+ --> $DIR/rename.rs:68:9
|
LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:66:9
+ --> $DIR/rename.rs:69:9
|
LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:67:9
+ --> $DIR/rename.rs:70:9
|
LL | #![warn(clippy::option_map_unwrap_or)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:68:9
+ --> $DIR/rename.rs:71:9
|
LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:69:9
+ --> $DIR/rename.rs:72:9
|
LL | #![warn(clippy::option_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
- --> $DIR/rename.rs:70:9
+ --> $DIR/rename.rs:73:9
|
LL | #![warn(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:71:9
+ --> $DIR/rename.rs:74:9
|
LL | #![warn(clippy::result_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:72:9
+ --> $DIR/rename.rs:75:9
|
LL | #![warn(clippy::result_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:73:9
+ --> $DIR/rename.rs:76:9
|
LL | #![warn(clippy::result_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
- --> $DIR/rename.rs:74:9
+ --> $DIR/rename.rs:77:9
|
LL | #![warn(clippy::single_char_push_str)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
- --> $DIR/rename.rs:75:9
+ --> $DIR/rename.rs:78:9
|
LL | #![warn(clippy::stutter)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
- --> $DIR/rename.rs:76:9
+ --> $DIR/rename.rs:79:9
|
LL | #![warn(clippy::to_string_in_display)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
- --> $DIR/rename.rs:77:9
+ --> $DIR/rename.rs:80:9
|
LL | #![warn(clippy::zero_width_space)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
+error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
+ --> $DIR/rename.rs:81:9
+ |
+LL | #![warn(clippy::cast_ref_to_mut)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
+
error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
- --> $DIR/rename.rs:78:9
+ --> $DIR/rename.rs:82:9
|
LL | #![warn(clippy::clone_double_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
+error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
+ --> $DIR/rename.rs:83:9
+ |
+LL | #![warn(clippy::cmp_nan)]
+ | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
+
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/rename.rs:79:9
+ --> $DIR/rename.rs:84:9
|
LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
- --> $DIR/rename.rs:80:9
+ --> $DIR/rename.rs:85:9
|
LL | #![warn(clippy::drop_copy)]
| ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
error: lint `clippy::drop_ref` has been renamed to `dropping_references`
- --> $DIR/rename.rs:81:9
+ --> $DIR/rename.rs:86:9
|
LL | #![warn(clippy::drop_ref)]
| ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:82:9
+ --> $DIR/rename.rs:87:9
|
LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:83:9
+ --> $DIR/rename.rs:88:9
|
LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:84:9
+ --> $DIR/rename.rs:89:9
|
LL | #![warn(clippy::for_loops_over_fallibles)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
- --> $DIR/rename.rs:85:9
+ --> $DIR/rename.rs:90:9
|
LL | #![warn(clippy::forget_copy)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
- --> $DIR/rename.rs:86:9
+ --> $DIR/rename.rs:91:9
|
LL | #![warn(clippy::forget_ref)]
| ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:87:9
+ --> $DIR/rename.rs:92:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/rename.rs:88:9
+ --> $DIR/rename.rs:93:9
|
LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:89:9
+ --> $DIR/rename.rs:94:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
+error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
+ --> $DIR/rename.rs:95:9
+ |
+LL | #![warn(clippy::invalid_utf8_in_unchecked)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
+
error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
- --> $DIR/rename.rs:90:9
+ --> $DIR/rename.rs:96:9
|
LL | #![warn(clippy::let_underscore_drop)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:91:9
+ --> $DIR/rename.rs:97:9
|
LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/rename.rs:92:9
+ --> $DIR/rename.rs:98:9
|
LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
- --> $DIR/rename.rs:93:9
+ --> $DIR/rename.rs:99:9
|
LL | #![warn(clippy::positional_named_format_parameters)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/rename.rs:94:9
+ --> $DIR/rename.rs:100:9
|
LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
+error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
+ --> $DIR/rename.rs:101:9
+ |
+LL | #![warn(clippy::undropped_manually_drops)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
+
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/rename.rs:95:9
+ --> $DIR/rename.rs:102:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/rename.rs:96:9
+ --> $DIR/rename.rs:103:9
|
LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
-error: aborting due to 48 previous errors
+error: aborting due to 52 previous errors
diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
index aea1507cc..a207e4221 100644
--- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
+++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
@@ -10,6 +10,8 @@
clippy::uninlined_format_args
)]
+use std::marker::ConstParamTy;
+
fn function() -> bool {
true
}
@@ -35,33 +37,33 @@ fn ifs_same_cond_fn() {
if function() {
} else if function() {
- //~ ERROR ifs same condition
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
if fn_arg(a) {
} else if fn_arg(a) {
- //~ ERROR ifs same condition
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
if obj.method() {
} else if obj.method() {
- //~ ERROR ifs same condition
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
if obj.method_arg(a) {
} else if obj.method_arg(a) {
- //~ ERROR ifs same condition
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
let mut v = vec![1];
if v.pop().is_none() {
- //~ ERROR ifs same condition
} else if v.pop().is_none() {
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
if v.len() == 42 {
- //~ ERROR ifs same condition
} else if v.len() == 42 {
+ //~^ ERROR: `if` has the same function call as a previous `if`
}
if v.len() == 1 {
@@ -96,7 +98,7 @@ fn main() {
};
println!("{}", os);
- #[derive(PartialEq, Eq)]
+ #[derive(PartialEq, Eq, ConstParamTy)]
enum E {
A,
B,
diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
index aade3b1fa..199e6769f 100644
--- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
+++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
@@ -1,11 +1,11 @@
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:37:15
+ --> $DIR/same_functions_in_if_condition.rs:39:15
|
LL | } else if function() {
| ^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:36:8
+ --> $DIR/same_functions_in_if_condition.rs:38:8
|
LL | if function() {
| ^^^^^^^^^^
@@ -16,61 +16,61 @@ LL | #![deny(clippy::same_functions_in_if_condition)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:42:15
+ --> $DIR/same_functions_in_if_condition.rs:44:15
|
LL | } else if fn_arg(a) {
| ^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:41:8
+ --> $DIR/same_functions_in_if_condition.rs:43:8
|
LL | if fn_arg(a) {
| ^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:47:15
+ --> $DIR/same_functions_in_if_condition.rs:49:15
|
LL | } else if obj.method() {
| ^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:46:8
+ --> $DIR/same_functions_in_if_condition.rs:48:8
|
LL | if obj.method() {
| ^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:52:15
+ --> $DIR/same_functions_in_if_condition.rs:54:15
|
LL | } else if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:51:8
+ --> $DIR/same_functions_in_if_condition.rs:53:8
|
LL | if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:59:15
+ --> $DIR/same_functions_in_if_condition.rs:60:15
|
LL | } else if v.pop().is_none() {
| ^^^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:57:8
+ --> $DIR/same_functions_in_if_condition.rs:59:8
|
LL | if v.pop().is_none() {
| ^^^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:64:15
+ --> $DIR/same_functions_in_if_condition.rs:65:15
|
LL | } else if v.len() == 42 {
| ^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:62:8
+ --> $DIR/same_functions_in_if_condition.rs:64:8
|
LL | if v.len() == 42 {
| ^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs
index 670599b0d..3cdbfaa16 100644
--- a/src/tools/clippy/tests/ui/search_is_some.rs
+++ b/src/tools/clippy/tests/ui/search_is_some.rs
@@ -1,5 +1,6 @@
//@aux-build:option_helpers.rs
#![warn(clippy::search_is_some)]
+#![allow(clippy::useless_vec)]
#![allow(dead_code)]
extern crate option_helpers;
use option_helpers::IteratorFalsePositives;
diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr
index 6bea8c674..7eff614d1 100644
--- a/src/tools/clippy/tests/ui/search_is_some.stderr
+++ b/src/tools/clippy/tests/ui/search_is_some.stderr
@@ -1,5 +1,5 @@
error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:14:13
+ --> $DIR/search_is_some.rs:15:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
@@ -12,7 +12,7 @@ LL | | ).is_some();
= note: `-D clippy::search-is-some` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some.rs:20:13
+ --> $DIR/search_is_some.rs:21:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
@@ -24,7 +24,7 @@ LL | | ).is_some();
= help: this is more succinctly expressed by calling `any()`
error: called `is_some()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some.rs:26:13
+ --> $DIR/search_is_some.rs:27:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
@@ -36,13 +36,13 @@ LL | | ).is_some();
= help: this is more succinctly expressed by calling `any()`
error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:41:20
+ --> $DIR/search_is_some.rs:42:20
|
LL | let _ = (0..1).find(some_closure).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:51:13
+ --> $DIR/search_is_some.rs:52:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
@@ -54,7 +54,7 @@ LL | | ).is_none();
= help: this is more succinctly expressed by calling `any()` with negation
error: called `is_none()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some.rs:57:13
+ --> $DIR/search_is_some.rs:58:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
@@ -66,7 +66,7 @@ LL | | ).is_none();
= help: this is more succinctly expressed by calling `any()` with negation
error: called `is_none()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some.rs:63:13
+ --> $DIR/search_is_some.rs:64:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
@@ -78,7 +78,7 @@ LL | | ).is_none();
= help: this is more succinctly expressed by calling `any()` with negation
error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:78:13
+ --> $DIR/search_is_some.rs:79:13
|
LL | let _ = (0..1).find(some_closure).is_none();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
index 9386618c1..08fb87cb3 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, clippy::explicit_auto_deref)]
+#![allow(dead_code, clippy::explicit_auto_deref, clippy::useless_vec)]
#![warn(clippy::search_is_some)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
index 6b2537a96..ec3386933 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, clippy::explicit_auto_deref)]
+#![allow(dead_code, clippy::explicit_auto_deref, clippy::useless_vec)]
#![warn(clippy::search_is_some)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
index e9116fc59..aa16f9da0 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, clippy::explicit_auto_deref)]
+#![allow(dead_code, clippy::explicit_auto_deref, clippy::useless_vec)]
#![warn(clippy::search_is_some)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
index b15283994..aeb6f118b 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, clippy::explicit_auto_deref)]
+#![allow(dead_code, clippy::explicit_auto_deref, clippy::useless_vec)]
#![warn(clippy::search_is_some)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/self_assignment.rs b/src/tools/clippy/tests/ui/self_assignment.rs
index ef6476229..ec3ae1209 100644
--- a/src/tools/clippy/tests/ui/self_assignment.rs
+++ b/src/tools/clippy/tests/ui/self_assignment.rs
@@ -1,4 +1,5 @@
#![warn(clippy::self_assignment)]
+#![allow(clippy::useless_vec)]
pub struct S<'a> {
a: i32,
diff --git a/src/tools/clippy/tests/ui/self_assignment.stderr b/src/tools/clippy/tests/ui/self_assignment.stderr
index 826e0d0ba..bed88244e 100644
--- a/src/tools/clippy/tests/ui/self_assignment.stderr
+++ b/src/tools/clippy/tests/ui/self_assignment.stderr
@@ -1,5 +1,5 @@
error: self-assignment of `a` to `a`
- --> $DIR/self_assignment.rs:12:5
+ --> $DIR/self_assignment.rs:13:5
|
LL | a = a;
| ^^^^^
@@ -7,61 +7,61 @@ LL | a = a;
= note: `-D clippy::self-assignment` implied by `-D warnings`
error: self-assignment of `*b` to `*b`
- --> $DIR/self_assignment.rs:13:5
+ --> $DIR/self_assignment.rs:14:5
|
LL | *b = *b;
| ^^^^^^^
error: self-assignment of `s` to `s`
- --> $DIR/self_assignment.rs:14:5
+ --> $DIR/self_assignment.rs:15:5
|
LL | s = s;
| ^^^^^
error: self-assignment of `s.a` to `s.a`
- --> $DIR/self_assignment.rs:15:5
+ --> $DIR/self_assignment.rs:16:5
|
LL | s.a = s.a;
| ^^^^^^^^^
error: self-assignment of `s.b[5 + 5]` to `s.b[10]`
- --> $DIR/self_assignment.rs:16:5
+ --> $DIR/self_assignment.rs:17:5
|
LL | s.b[10] = s.b[5 + 5];
| ^^^^^^^^^^^^^^^^^^^^
error: self-assignment of `s.c[0][1]` to `s.c[0][1]`
- --> $DIR/self_assignment.rs:17:5
+ --> $DIR/self_assignment.rs:18:5
|
LL | s.c[0][1] = s.c[0][1];
| ^^^^^^^^^^^^^^^^^^^^^
error: self-assignment of `s.b[a]` to `s.b[a]`
- --> $DIR/self_assignment.rs:18:5
+ --> $DIR/self_assignment.rs:19:5
|
LL | s.b[a] = s.b[a];
| ^^^^^^^^^^^^^^^
error: self-assignment of `*s.e` to `*s.e`
- --> $DIR/self_assignment.rs:19:5
+ --> $DIR/self_assignment.rs:20:5
|
LL | *s.e = *s.e;
| ^^^^^^^^^^^
error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]`
- --> $DIR/self_assignment.rs:20:5
+ --> $DIR/self_assignment.rs:21:5
|
LL | s.b[a + 10] = s.b[10 + a];
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: self-assignment of `t.1` to `t.1`
- --> $DIR/self_assignment.rs:23:5
+ --> $DIR/self_assignment.rs:24:5
|
LL | t.1 = t.1;
| ^^^^^^^^^
error: self-assignment of `(t.0)` to `t.0`
- --> $DIR/self_assignment.rs:24:5
+ --> $DIR/self_assignment.rs:25:5
|
LL | t.0 = (t.0);
| ^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs
index 2c0fc3e3f..9be8c5e59 100644
--- a/src/tools/clippy/tests/ui/shadow.rs
+++ b/src/tools/clippy/tests/ui/shadow.rs
@@ -1,7 +1,7 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::needless_if)]
extern crate proc_macro_derive;
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.fixed b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.fixed
new file mode 100644
index 000000000..acc78d6bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.fixed
@@ -0,0 +1,627 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+// //@run-rustfix
+#![warn(clippy::significant_drop_in_scrutinee)]
+#![allow(dead_code, unused_assignments)]
+#![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)]
+
+use std::num::ParseIntError;
+use std::ops::Deref;
+use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::RwLock;
+use std::sync::{Mutex, MutexGuard};
+
+struct State {}
+
+impl State {
+ fn foo(&self) -> bool {
+ true
+ }
+
+ fn bar(&self) {}
+}
+
+fn should_not_trigger_lint_with_mutex_guard_outside_match() {
+ let mutex = Mutex::new(State {});
+
+ // Should not trigger lint because the temporary should drop at the `;` on line before the match
+ let is_foo = mutex.lock().unwrap().foo();
+ match is_foo {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
+ let mutex = Mutex::new(State {});
+
+ // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
+ // so its lifetime should not be surprising.
+ match mutex.lock() {
+ Ok(guard) => {
+ guard.foo();
+ mutex.lock().unwrap().bar();
+ },
+ _ => {},
+ };
+}
+
+fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
+ let mutex = Mutex::new(State {});
+
+ // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
+ // is preserved until the end of the match, but there is no clear indication that this is the
+ // case.
+ match mutex.lock().unwrap().foo() {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_with_mutex_guard_in_match_scrutinee_when_lint_allowed() {
+ let mutex = Mutex::new(State {});
+
+ // Lint should not be triggered because it is "allowed" below.
+ #[allow(clippy::significant_drop_in_scrutinee)]
+ match mutex.lock().unwrap().foo() {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_insignificant_drop() {
+ // Should not trigger lint because there are no temporaries whose drops have a significant
+ // side effect.
+ match 1u64.to_string().is_empty() {
+ true => {
+ println!("It was empty")
+ },
+ false => {
+ println!("It was not empty")
+ },
+ }
+}
+
+struct StateWithMutex {
+ m: Mutex<u64>,
+}
+
+struct MutexGuardWrapper<'a> {
+ mg: MutexGuard<'a, u64>,
+}
+
+impl<'a> MutexGuardWrapper<'a> {
+ fn get_the_value(&self) -> u64 {
+ *self.mg.deref()
+ }
+}
+
+struct MutexGuardWrapperWrapper<'a> {
+ mg: MutexGuardWrapper<'a>,
+}
+
+impl<'a> MutexGuardWrapperWrapper<'a> {
+ fn get_the_value(&self) -> u64 {
+ *self.mg.mg.deref()
+ }
+}
+
+impl StateWithMutex {
+ fn lock_m(&self) -> MutexGuardWrapper<'_> {
+ MutexGuardWrapper {
+ mg: self.m.lock().unwrap(),
+ }
+ }
+
+ fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
+ MutexGuardWrapperWrapper {
+ mg: MutexGuardWrapper {
+ mg: self.m.lock().unwrap(),
+ },
+ }
+ }
+
+ fn foo(&self) -> bool {
+ true
+ }
+
+ fn bar(&self) {}
+}
+
+fn should_trigger_lint_with_wrapped_mutex() {
+ let s = StateWithMutex { m: Mutex::new(1) };
+
+ // Should trigger lint because a temporary contains a type with a significant drop and its
+ // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
+ // the temporary contains such a type, making it potentially even more surprising.
+ match s.lock_m().get_the_value() {
+ 1 => {
+ println!("Got 1. Is it still 1?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ 2 => {
+ println!("Got 2. Is it still 2?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ _ => {},
+ }
+ println!("All done!");
+}
+
+fn should_trigger_lint_with_double_wrapped_mutex() {
+ let s = StateWithMutex { m: Mutex::new(1) };
+
+ // Should trigger lint because a temporary contains a type which further contains a type with a
+ // significant drop and its lifetime is not obvious. Additionally, it is not obvious from
+ // looking at the scrutinee that the temporary contains such a type, making it potentially even
+ // more surprising.
+ match s.lock_m_m().get_the_value() {
+ 1 => {
+ println!("Got 1. Is it still 1?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ 2 => {
+ println!("Got 2. Is it still 2?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ _ => {},
+ }
+ println!("All done!");
+}
+
+struct Counter {
+ i: AtomicU64,
+}
+
+#[clippy::has_significant_drop]
+struct CounterWrapper<'a> {
+ counter: &'a Counter,
+}
+
+impl<'a> CounterWrapper<'a> {
+ fn new(counter: &Counter) -> CounterWrapper {
+ counter.i.fetch_add(1, Ordering::Relaxed);
+ CounterWrapper { counter }
+ }
+}
+
+impl<'a> Drop for CounterWrapper<'a> {
+ fn drop(&mut self) {
+ self.counter.i.fetch_sub(1, Ordering::Relaxed);
+ }
+}
+
+impl Counter {
+ fn temp_increment(&self) -> Vec<CounterWrapper> {
+ vec![CounterWrapper::new(self), CounterWrapper::new(self)]
+ }
+}
+
+fn should_trigger_lint_for_vec() {
+ let counter = Counter { i: AtomicU64::new(0) };
+
+ // Should trigger lint because the temporary in the scrutinee returns a collection of types
+ // which have significant drops. The types with significant drops are also non-obvious when
+ // reading the expression in the scrutinee.
+ match counter.temp_increment().len() {
+ 2 => {
+ let current_count = counter.i.load(Ordering::Relaxed);
+ println!("Current count {}", current_count);
+ assert_eq!(current_count, 0);
+ },
+ 1 => {},
+ 3 => {},
+ _ => {},
+ };
+}
+
+struct StateWithField {
+ s: String,
+}
+
+// Should trigger lint only on the type in the tuple which is created using a temporary
+// with a significant drop. Additionally, this test ensures that the format of the tuple
+// is preserved correctly in the suggestion.
+fn should_trigger_lint_for_tuple_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ {
+ match (mutex1.lock().unwrap().s.len(), true) {
+ (3, _) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _) => {},
+ };
+
+ match (true, mutex1.lock().unwrap().s.len(), true) {
+ (_, 3, _) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _, _) => {},
+ };
+
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+ match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
+ (3, _, 3) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _, _) => {},
+ };
+
+ let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
+ match mutex3.lock().unwrap().s.as_str() {
+ "three" => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ _ => {},
+ };
+
+ match (true, mutex3.lock().unwrap().s.as_str()) {
+ (_, "three") => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _) => {},
+ };
+ }
+}
+
+// Should trigger lint when either side of a binary operation creates a temporary with a
+// significant drop.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
+ let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
+
+ match mutex.lock().unwrap().s.len() > 1 {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+
+ match 1 < mutex.lock().unwrap().s.len() {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+// Should trigger lint when both sides of a binary operation creates a temporary with a
+// significant drop.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
+ let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
+ let mutex2 = Mutex::new(StateWithField {
+ s: "statewithfield".to_owned(),
+ });
+
+ match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
+ true => {
+ println!(
+ "{} < {}",
+ mutex1.lock().unwrap().s.len(),
+ mutex2.lock().unwrap().s.len()
+ );
+ },
+ false => {},
+ };
+
+ match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
+ true => {
+ println!(
+ "{} >= {}",
+ mutex1.lock().unwrap().s.len(),
+ mutex2.lock().unwrap().s.len()
+ );
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_closure_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ let get_mutex_guard = || mutex1.lock().unwrap().s.len();
+
+ // Should not trigger lint because the temporary with a significant drop will be dropped
+ // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
+ // surprising lifetime.
+ match get_mutex_guard() > 1 {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ let get_mutex_guard = || mutex1.lock().unwrap();
+
+ // Should trigger lint because the temporary with a significant drop is returned from the
+ // closure but not used directly in any match arms, so it has a potentially surprising lifetime.
+ match get_mutex_guard().s.len() > 1 {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_trigger_lint_for_return_from_match_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+
+ let i = 100;
+
+ // Should trigger lint because the nested match within the scrutinee returns a temporary with a
+ // significant drop is but not used directly in any match arms, so it has a potentially
+ // surprising lifetime.
+ match match i {
+ 100 => mutex1.lock().unwrap(),
+ _ => mutex2.lock().unwrap(),
+ }
+ .s
+ .len()
+ > 1
+ {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {
+ println!("nothing to do here");
+ },
+ };
+}
+
+fn should_trigger_lint_for_return_from_if_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+
+ let i = 100;
+
+ // Should trigger lint because the nested if-expression within the scrutinee returns a temporary
+ // with a significant drop is but not used directly in any match arms, so it has a potentially
+ // surprising lifetime.
+ match if i > 1 {
+ mutex1.lock().unwrap()
+ } else {
+ mutex2.lock().unwrap()
+ }
+ .s
+ .len()
+ > 1
+ {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_if_in_scrutinee() {
+ let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
+
+ let i = 100;
+
+ // Should not trigger the lint because the temporary with a significant drop *is* dropped within
+ // the body of the if-expression nested within the match scrutinee, and therefore does not have
+ // a potentially surprising lifetime.
+ match if i > 1 {
+ mutex.lock().unwrap().s.len() > 1
+ } else {
+ false
+ } {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+struct StateWithBoxedMutexGuard {
+ u: Mutex<u64>,
+}
+
+impl StateWithBoxedMutexGuard {
+ fn new() -> StateWithBoxedMutexGuard {
+ StateWithBoxedMutexGuard { u: Mutex::new(42) }
+ }
+ fn lock(&self) -> Box<MutexGuard<u64>> {
+ Box::new(self.u.lock().unwrap())
+ }
+}
+
+fn should_trigger_lint_for_boxed_mutex_guard() {
+ let s = StateWithBoxedMutexGuard::new();
+
+ // Should trigger lint because a temporary Box holding a type with a significant drop in a match
+ // scrutinee may have a potentially surprising lifetime.
+ match s.lock().deref().deref() {
+ 0 | 1 => println!("Value was less than 2"),
+ _ => println!("Value is {}", s.lock().deref()),
+ };
+}
+
+struct StateStringWithBoxedMutexGuard {
+ s: Mutex<String>,
+}
+
+impl StateStringWithBoxedMutexGuard {
+ fn new() -> StateStringWithBoxedMutexGuard {
+ StateStringWithBoxedMutexGuard {
+ s: Mutex::new("A String".to_owned()),
+ }
+ }
+ fn lock(&self) -> Box<MutexGuard<String>> {
+ Box::new(self.s.lock().unwrap())
+ }
+}
+
+fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
+ let s = StateStringWithBoxedMutexGuard::new();
+
+ let matcher = String::from("A String");
+
+ // Should trigger lint because a temporary Box holding a type with a significant drop in a match
+ // scrutinee may have a potentially surprising lifetime.
+ match s.lock().deref().deref() {
+ matcher => println!("Value is {}", s.lock().deref()),
+ _ => println!("Value was not a match"),
+ };
+}
+
+struct StateWithIntField {
+ i: u64,
+}
+
+// Should trigger lint when either side of an assign expression contains a temporary with a
+// significant drop, because the temporary's lifetime will be extended to the end of the match.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_in_assign_expr() {
+ let mutex = Mutex::new(StateWithIntField { i: 10 });
+
+ let mut i = 100;
+
+ match mutex.lock().unwrap().i = i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match i = mutex.lock().unwrap().i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match mutex.lock().unwrap().i += 1 {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match i += mutex.lock().unwrap().i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+}
+
+#[derive(Debug)]
+enum RecursiveEnum {
+ Foo(Option<Box<RecursiveEnum>>),
+}
+
+#[derive(Debug)]
+enum GenericRecursiveEnum<T> {
+ Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
+}
+
+fn should_not_cause_stack_overflow() {
+ // Test that when a type recursively contains itself, a stack overflow does not occur when
+ // checking sub-types for significant drops.
+ let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
+ match f {
+ RecursiveEnum::Foo(Some(f)) => {
+ println!("{:?}", f)
+ },
+ RecursiveEnum::Foo(f) => {
+ println!("{:?}", f)
+ },
+ }
+
+ let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
+ match f {
+ GenericRecursiveEnum::Foo(i, Some(f)) => {
+ println!("{} {:?}", i, f)
+ },
+ GenericRecursiveEnum::Foo(i, f) => {
+ println!("{} {:?}", i, f)
+ },
+ }
+}
+
+fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
+ // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
+ // for this lint
+ let rwlock = RwLock::new("1".to_string());
+ let result = rwlock.read().unwrap().parse::<u64>()?;
+ println!("{}", result);
+ rwlock.write().unwrap().push('2');
+ Ok(result)
+}
+
+struct ResultReturner {
+ s: String,
+}
+
+impl ResultReturner {
+ fn to_number(&self) -> Result<i64, ParseIntError> {
+ self.s.parse::<i64>()
+ }
+}
+
+fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
+ let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
+ match rwlock.read().unwrap().to_number() {
+ Ok(n) => println!("Converted to number: {}", n),
+ Err(e) => println!("Could not convert {} to number", e),
+ };
+}
+
+fn should_trigger_lint_for_read_write_lock_for_loop() {
+ // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
+ // designed to look for.
+ let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
+ for s in rwlock.read().unwrap().iter() {
+ println!("{}", s);
+ }
+}
+
+fn do_bar(mutex: &Mutex<State>) {
+ mutex.lock().unwrap().bar();
+}
+
+fn should_trigger_lint_without_significant_drop_in_arm() {
+ let mutex = Mutex::new(State {});
+
+ // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
+ // is preserved until the end of the match, but there is no clear indication that this is the
+ // case.
+ match mutex.lock().unwrap().foo() {
+ true => do_bar(&mutex),
+ false => {},
+ };
+}
+
+fn should_not_trigger_on_significant_iterator_drop() {
+ let lines = std::io::stdin().lines();
+ for line in lines {
+ println!("foo: {}", line.unwrap());
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed
index ee7f2b063..7b848ead7 100644
--- a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed
+++ b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed
@@ -16,6 +16,18 @@ pub fn complex_return_triggers_the_lint() -> i32 {
foo()
}
+pub fn issue_10413() {
+ let mutex = Mutex::new(Some(1));
+ let opt = Some(1);
+ if opt.is_some() {
+ let lock = mutex.lock().unwrap();
+ let _ = *lock;
+ if opt.is_some() {
+ let _ = *lock;
+ }
+ }
+}
+
pub fn path_return_can_be_ignored() -> i32 {
let mutex = Mutex::new(1);
let lock = mutex.lock().unwrap();
diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.rs b/src/tools/clippy/tests/ui/significant_drop_tightening.rs
index 9c139deb9..36f77cf1b 100644
--- a/src/tools/clippy/tests/ui/significant_drop_tightening.rs
+++ b/src/tools/clippy/tests/ui/significant_drop_tightening.rs
@@ -15,6 +15,18 @@ pub fn complex_return_triggers_the_lint() -> i32 {
foo()
}
+pub fn issue_10413() {
+ let mutex = Mutex::new(Some(1));
+ let opt = Some(1);
+ if opt.is_some() {
+ let lock = mutex.lock().unwrap();
+ let _ = *lock;
+ if opt.is_some() {
+ let _ = *lock;
+ }
+ }
+}
+
pub fn path_return_can_be_ignored() -> i32 {
let mutex = Mutex::new(1);
let lock = mutex.lock().unwrap();
diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr
index ab8ce356e..3bdac0b0a 100644
--- a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr
+++ b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr
@@ -23,7 +23,7 @@ LL + drop(lock);
|
error: temporary with significant `Drop` can be early dropped
- --> $DIR/significant_drop_tightening.rs:44:13
+ --> $DIR/significant_drop_tightening.rs:56:13
|
LL | / {
LL | | let mutex = Mutex::new(1i32);
@@ -43,7 +43,7 @@ LL + drop(lock);
|
error: temporary with significant `Drop` can be early dropped
- --> $DIR/significant_drop_tightening.rs:65:13
+ --> $DIR/significant_drop_tightening.rs:77:13
|
LL | / {
LL | | let mutex = Mutex::new(1i32);
@@ -67,7 +67,7 @@ LL +
|
error: temporary with significant `Drop` can be early dropped
- --> $DIR/significant_drop_tightening.rs:71:17
+ --> $DIR/significant_drop_tightening.rs:83:17
|
LL | / {
LL | | let mutex = Mutex::new(vec![1i32]);
diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs
new file mode 100644
index 000000000..76e175014
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_call_fn.rs
@@ -0,0 +1,74 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(clippy::redundant_closure_call, unused)]
+#![warn(clippy::single_call_fn)]
+#![no_main]
+
+#[macro_use]
+extern crate proc_macros;
+
+// Do not lint since it's public
+pub fn f() {}
+
+fn i() {}
+fn j() {}
+
+fn h() {
+ // Linted
+ let a = i;
+ // Do not lint closures
+ let a = (|| {
+ // Not linted
+ a();
+ // Imo, it's reasonable to lint this as the function is still only being used once. Just in
+ // a closure.
+ j();
+ });
+ a();
+}
+
+fn g() {
+ f();
+}
+
+fn c() {
+ println!("really");
+ println!("long");
+ println!("function...");
+}
+
+fn d() {
+ c();
+}
+
+fn a() {}
+
+fn b() {
+ a();
+
+ external! {
+ fn lol() {
+ lol_inner();
+ }
+ fn lol_inner() {}
+ }
+ with_span! {
+ span
+ fn lol2() {
+ lol2_inner();
+ }
+ fn lol2_inner() {}
+ }
+}
+
+fn e() {
+ b();
+ b();
+}
+
+#[test]
+fn k() {}
+
+#[test]
+fn l() {
+ k();
+}
diff --git a/src/tools/clippy/tests/ui/single_call_fn.stderr b/src/tools/clippy/tests/ui/single_call_fn.stderr
new file mode 100644
index 000000000..9ef8c4878
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_call_fn.stderr
@@ -0,0 +1,55 @@
+error: this function is only used once
+ --> $DIR/single_call_fn.rs:33:1
+ |
+LL | / fn c() {
+LL | | println!("really");
+LL | | println!("long");
+LL | | println!("function...");
+LL | | }
+ | |_^
+ |
+help: used here
+ --> $DIR/single_call_fn.rs:40:5
+ |
+LL | c();
+ | ^
+ = note: `-D clippy::single-call-fn` implied by `-D warnings`
+
+error: this function is only used once
+ --> $DIR/single_call_fn.rs:12:1
+ |
+LL | fn i() {}
+ | ^^^^^^^^^
+ |
+help: used here
+ --> $DIR/single_call_fn.rs:17:13
+ |
+LL | let a = i;
+ | ^
+
+error: this function is only used once
+ --> $DIR/single_call_fn.rs:43:1
+ |
+LL | fn a() {}
+ | ^^^^^^^^^
+ |
+help: used here
+ --> $DIR/single_call_fn.rs:46:5
+ |
+LL | a();
+ | ^
+
+error: this function is only used once
+ --> $DIR/single_call_fn.rs:13:1
+ |
+LL | fn j() {}
+ | ^^^^^^^^^
+ |
+help: used here
+ --> $DIR/single_call_fn.rs:24:9
+ |
+LL | j();
+ | ^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.fixed b/src/tools/clippy/tests/ui/single_char_add_str.fixed
index cbcf1ab21..cb301c8bc 100644
--- a/src/tools/clippy/tests/ui/single_char_add_str.fixed
+++ b/src/tools/clippy/tests/ui/single_char_add_str.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::single_char_add_str)]
+#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes)]
macro_rules! get_string {
() => {
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.rs b/src/tools/clippy/tests/ui/single_char_add_str.rs
index a1f005cc8..99baf35ac 100644
--- a/src/tools/clippy/tests/ui/single_char_add_str.rs
+++ b/src/tools/clippy/tests/ui/single_char_add_str.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::single_char_add_str)]
+#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes)]
macro_rules! get_string {
() => {
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.stderr b/src/tools/clippy/tests/ui/single_char_add_str.stderr
index 55d91583a..3f93c1847 100644
--- a/src/tools/clippy/tests/ui/single_char_add_str.stderr
+++ b/src/tools/clippy/tests/ui/single_char_add_str.stderr
@@ -1,5 +1,5 @@
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:14:5
+ --> $DIR/single_char_add_str.rs:15:5
|
LL | string.push_str("R");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
@@ -7,85 +7,85 @@ LL | string.push_str("R");
= note: `-D clippy::single-char-add-str` implied by `-D warnings`
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:15:5
+ --> $DIR/single_char_add_str.rs:16:5
|
LL | string.push_str("'");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:20:5
+ --> $DIR/single_char_add_str.rs:21:5
|
LL | string.push_str("/x52");
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:21:5
+ --> $DIR/single_char_add_str.rs:22:5
|
LL | string.push_str("/u{0052}");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:22:5
+ --> $DIR/single_char_add_str.rs:23:5
|
LL | string.push_str(r##"a"##);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:24:5
+ --> $DIR/single_char_add_str.rs:25:5
|
LL | get_string!().push_str("ö");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:29:5
+ --> $DIR/single_char_add_str.rs:30:5
|
LL | string.insert_str(0, "R");
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:30:5
+ --> $DIR/single_char_add_str.rs:31:5
|
LL | string.insert_str(1, "'");
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:35:5
+ --> $DIR/single_char_add_str.rs:36:5
|
LL | string.insert_str(0, "/x52");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:36:5
+ --> $DIR/single_char_add_str.rs:37:5
|
LL | string.insert_str(0, "/u{0052}");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:38:5
+ --> $DIR/single_char_add_str.rs:39:5
|
LL | string.insert_str(x, r##"a"##);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:40:5
+ --> $DIR/single_char_add_str.rs:41:5
|
LL | string.insert_str(Y, r##"a"##);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:41:5
+ --> $DIR/single_char_add_str.rs:42:5
|
LL | string.insert_str(Y, r##"""##);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:42:5
+ --> $DIR/single_char_add_str.rs:43:5
|
LL | string.insert_str(Y, r##"'"##);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')`
error: calling `insert_str()` using a single-character string literal
- --> $DIR/single_char_add_str.rs:44:5
+ --> $DIR/single_char_add_str.rs:45:5
|
LL | get_string!().insert_str(1, "?");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')`
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed
index dba898720..7ae62231a 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.fixed
+++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused_must_use)]
+#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)]
use std::collections::HashSet;
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs
index 6a145a14b..0604624e7 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.rs
+++ b/src/tools/clippy/tests/ui/single_char_pattern.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused_must_use)]
+#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)]
use std::collections::HashSet;
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr
index 5564aac67..5ae2450c2 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.stderr
+++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr
@@ -226,13 +226,13 @@ error: single-character string constant used as pattern
--> $DIR/single_char_pattern.rs:65:13
|
LL | x.split(r#"/"#);
- | ^^^^^^ help: try using a `char` instead: `'/'`
+ | ^^^^^^ help: try using a `char` instead: `'//'`
error: single-character string constant used as pattern
--> $DIR/single_char_pattern.rs:66:13
|
LL | x.split(r"/");
- | ^^^^ help: try using a `char` instead: `'/'`
+ | ^^^^ help: try using a `char` instead: `'//'`
error: aborting due to 39 previous errors
diff --git a/src/tools/clippy/tests/ui/single_element_loop.fixed b/src/tools/clippy/tests/ui/single_element_loop.fixed
index 1697a0cf2..598f25941 100644
--- a/src/tools/clippy/tests/ui/single_element_loop.fixed
+++ b/src/tools/clippy/tests/ui/single_element_loop.fixed
@@ -1,6 +1,8 @@
//@run-rustfix
// Tests from for_loop.rs that don't have suggestions
+#![allow(clippy::single_range_in_vec_init)]
+
#[warn(clippy::single_element_loop)]
fn main() {
let item1 = 2;
diff --git a/src/tools/clippy/tests/ui/single_element_loop.rs b/src/tools/clippy/tests/ui/single_element_loop.rs
index 860424f42..3fc461735 100644
--- a/src/tools/clippy/tests/ui/single_element_loop.rs
+++ b/src/tools/clippy/tests/ui/single_element_loop.rs
@@ -1,6 +1,8 @@
//@run-rustfix
// Tests from for_loop.rs that don't have suggestions
+#![allow(clippy::single_range_in_vec_init)]
+
#[warn(clippy::single_element_loop)]
fn main() {
let item1 = 2;
diff --git a/src/tools/clippy/tests/ui/single_element_loop.stderr b/src/tools/clippy/tests/ui/single_element_loop.stderr
index 14437a597..c40c61989 100644
--- a/src/tools/clippy/tests/ui/single_element_loop.stderr
+++ b/src/tools/clippy/tests/ui/single_element_loop.stderr
@@ -1,5 +1,5 @@
error: for loop over a single element
- --> $DIR/single_element_loop.rs:7:5
+ --> $DIR/single_element_loop.rs:9:5
|
LL | / for item in &[item1] {
LL | | dbg!(item);
@@ -16,7 +16,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:11:5
+ --> $DIR/single_element_loop.rs:13:5
|
LL | / for item in [item1].iter() {
LL | | dbg!(item);
@@ -32,7 +32,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:15:5
+ --> $DIR/single_element_loop.rs:17:5
|
LL | / for item in &[0..5] {
LL | | dbg!(item);
@@ -48,7 +48,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:19:5
+ --> $DIR/single_element_loop.rs:21:5
|
LL | / for item in [0..5].iter_mut() {
LL | | dbg!(item);
@@ -64,7 +64,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:23:5
+ --> $DIR/single_element_loop.rs:25:5
|
LL | / for item in [0..5] {
LL | | dbg!(item);
@@ -80,7 +80,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:27:5
+ --> $DIR/single_element_loop.rs:29:5
|
LL | / for item in [0..5].into_iter() {
LL | | dbg!(item);
@@ -96,7 +96,7 @@ LL + }
|
error: for loop over a single element
- --> $DIR/single_element_loop.rs:46:5
+ --> $DIR/single_element_loop.rs:48:5
|
LL | / for _ in [42] {
LL | | let _f = |n: u32| {
diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed
new file mode 100644
index 000000000..e7b1fd6a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match.fixed
@@ -0,0 +1,254 @@
+//@run-rustfix
+#![warn(clippy::single_match)]
+#![allow(
+ unused,
+ clippy::uninlined_format_args,
+ clippy::needless_if,
+ clippy::redundant_pattern_matching
+)]
+fn dummy() {}
+
+fn single_match() {
+ let x = Some(1u8);
+
+ if let Some(y) = x {
+ println!("{:?}", y);
+ };
+
+ let x = Some(1u8);
+ if let Some(y) = x { println!("{:?}", y) }
+
+ let z = (1u8, 1u8);
+ if let (2..=3, 7..=9) = z { dummy() };
+
+ // Not linted (pattern guards used)
+ match x {
+ Some(y) if y == 0 => println!("{:?}", y),
+ _ => (),
+ }
+
+ // Not linted (no block with statements in the single arm)
+ match z {
+ (2..=3, 7..=9) => println!("{:?}", z),
+ _ => println!("nope"),
+ }
+}
+
+enum Foo {
+ Bar,
+ Baz(u8),
+}
+use std::borrow::Cow;
+use Foo::*;
+
+fn single_match_know_enum() {
+ let x = Some(1u8);
+ let y: Result<_, i8> = Ok(1i8);
+
+ if let Some(y) = x { dummy() };
+
+ if let Ok(y) = y { dummy() };
+
+ let c = Cow::Borrowed("");
+
+ if let Cow::Borrowed(..) = c { dummy() };
+
+ let z = Foo::Bar;
+ // no warning
+ match z {
+ Bar => println!("42"),
+ Baz(_) => (),
+ }
+
+ match z {
+ Baz(_) => println!("42"),
+ Bar => (),
+ }
+}
+
+// issue #173
+fn if_suggestion() {
+ let x = "test";
+ if x == "test" { println!() }
+
+ #[derive(PartialEq, Eq)]
+ enum Foo {
+ A,
+ B,
+ C(u32),
+ }
+
+ let x = Foo::A;
+ if x == Foo::A { println!() }
+
+ const FOO_C: Foo = Foo::C(0);
+ if x == FOO_C { println!() }
+
+ if x == Foo::A { println!() }
+
+ let x = &x;
+ if x == &Foo::A { println!() }
+
+ enum Bar {
+ A,
+ B,
+ }
+ impl PartialEq for Bar {
+ fn eq(&self, rhs: &Self) -> bool {
+ matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B))
+ }
+ }
+ impl Eq for Bar {}
+
+ let x = Bar::A;
+ if let Bar::A = x { println!() }
+
+ // issue #7038
+ struct X;
+ let x = Some(X);
+ if let None = x { println!() };
+}
+
+// See: issue #8282
+fn ranges() {
+ enum E {
+ V,
+ }
+ let x = (Some(E::V), Some(42));
+
+ // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the
+ // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared
+ // because of `match` construction.
+ match x {
+ (Some(E::V), _) => {},
+ (None, _) => {},
+ }
+
+ // lint
+ if let (Some(_), _) = x {}
+
+ // lint
+ if let (Some(E::V), _) = x { todo!() }
+
+ // lint
+ if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (.., Some(E::V), _) => {},
+ (.., None, _) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (Some(E::V), ..) => {},
+ (None, ..) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (_, Some(E::V), ..) => {},
+ (_, None, ..) => {},
+ }
+}
+
+fn skip_type_aliases() {
+ enum OptionEx {
+ Some(i32),
+ None,
+ }
+ enum ResultEx {
+ Err(i32),
+ Ok(i32),
+ }
+
+ use OptionEx::{None, Some};
+ use ResultEx::{Err, Ok};
+
+ // don't lint
+ match Err(42) {
+ Ok(_) => dummy(),
+ Err(_) => (),
+ };
+
+ // don't lint
+ match Some(1i32) {
+ Some(_) => dummy(),
+ None => (),
+ };
+}
+
+macro_rules! single_match {
+ ($num:literal) => {
+ match $num {
+ 15 => println!("15"),
+ _ => (),
+ }
+ };
+}
+
+fn main() {
+ single_match!(5);
+
+ // Don't lint
+ let _ = match Some(0) {
+ #[cfg(feature = "foo")]
+ Some(10) => 11,
+ Some(x) => x,
+ _ => 0,
+ };
+}
+
+fn issue_10808(bar: Option<i32>) {
+ if let Some(v) = bar { unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ } }
+
+ if let Some(v) = bar {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ }
+}
+
+mod issue8634 {
+ struct SomeError(i32, i32);
+
+ fn foo(x: Result<i32, ()>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(()) => {
+ // Ignore this error because blah blah blah.
+ },
+ }
+ }
+
+ fn bar(x: Result<i32, SomeError>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(_) => {
+ // TODO: Process the error properly.
+ },
+ }
+ }
+
+ fn block_comment(x: Result<i32, SomeError>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(_) => {
+ /*
+ let's make sure that this also
+ does not lint block comments.
+ */
+ },
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs
index d0c9b7b56..1515a7053 100644
--- a/src/tools/clippy/tests/ui/single_match.rs
+++ b/src/tools/clippy/tests/ui/single_match.rs
@@ -1,6 +1,11 @@
+//@run-rustfix
#![warn(clippy::single_match)]
-#![allow(clippy::uninlined_format_args)]
-
+#![allow(
+ unused,
+ clippy::uninlined_format_args,
+ clippy::needless_if,
+ clippy::redundant_pattern_matching
+)]
fn dummy() {}
fn single_match() {
@@ -244,3 +249,64 @@ fn main() {
_ => 0,
};
}
+
+fn issue_10808(bar: Option<i32>) {
+ match bar {
+ Some(v) => unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ },
+ _ => {},
+ }
+
+ match bar {
+ #[rustfmt::skip]
+ Some(v) => {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ _ => {},
+ }
+}
+
+mod issue8634 {
+ struct SomeError(i32, i32);
+
+ fn foo(x: Result<i32, ()>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(()) => {
+ // Ignore this error because blah blah blah.
+ },
+ }
+ }
+
+ fn bar(x: Result<i32, SomeError>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(_) => {
+ // TODO: Process the error properly.
+ },
+ }
+ }
+
+ fn block_comment(x: Result<i32, SomeError>) {
+ match x {
+ Ok(y) => {
+ println!("Yay! {y}");
+ },
+ Err(_) => {
+ /*
+ let's make sure that this also
+ does not lint block comments.
+ */
+ },
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr
index 7cecc1b73..ef9015132 100644
--- a/src/tools/clippy/tests/ui/single_match.stderr
+++ b/src/tools/clippy/tests/ui/single_match.stderr
@@ -1,5 +1,5 @@
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:9:5
+ --> $DIR/single_match.rs:14:5
|
LL | / match x {
LL | | Some(y) => {
@@ -18,7 +18,7 @@ LL ~ };
|
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:17:5
+ --> $DIR/single_match.rs:22:5
|
LL | / match x {
LL | | // Note the missing block braces.
@@ -30,7 +30,7 @@ LL | | }
| |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:26:5
+ --> $DIR/single_match.rs:31:5
|
LL | / match z {
LL | | (2..=3, 7..=9) => dummy(),
@@ -39,7 +39,7 @@ LL | | };
| |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:55:5
+ --> $DIR/single_match.rs:60:5
|
LL | / match x {
LL | | Some(y) => dummy(),
@@ -48,7 +48,7 @@ LL | | };
| |_____^ help: try this: `if let Some(y) = x { dummy() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:60:5
+ --> $DIR/single_match.rs:65:5
|
LL | / match y {
LL | | Ok(y) => dummy(),
@@ -57,7 +57,7 @@ LL | | };
| |_____^ help: try this: `if let Ok(y) = y { dummy() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:67:5
+ --> $DIR/single_match.rs:72:5
|
LL | / match c {
LL | | Cow::Borrowed(..) => dummy(),
@@ -66,7 +66,7 @@ LL | | };
| |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
- --> $DIR/single_match.rs:88:5
+ --> $DIR/single_match.rs:93:5
|
LL | / match x {
LL | | "test" => println!(),
@@ -75,7 +75,7 @@ LL | | }
| |_____^ help: try this: `if x == "test" { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
- --> $DIR/single_match.rs:101:5
+ --> $DIR/single_match.rs:106:5
|
LL | / match x {
LL | | Foo::A => println!(),
@@ -84,7 +84,7 @@ LL | | }
| |_____^ help: try this: `if x == Foo::A { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
- --> $DIR/single_match.rs:107:5
+ --> $DIR/single_match.rs:112:5
|
LL | / match x {
LL | | FOO_C => println!(),
@@ -93,7 +93,7 @@ LL | | }
| |_____^ help: try this: `if x == FOO_C { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
- --> $DIR/single_match.rs:112:5
+ --> $DIR/single_match.rs:117:5
|
LL | / match &&x {
LL | | Foo::A => println!(),
@@ -102,7 +102,7 @@ LL | | }
| |_____^ help: try this: `if x == Foo::A { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
- --> $DIR/single_match.rs:118:5
+ --> $DIR/single_match.rs:123:5
|
LL | / match &x {
LL | | Foo::A => println!(),
@@ -111,7 +111,7 @@ LL | | }
| |_____^ help: try this: `if x == &Foo::A { println!() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:135:5
+ --> $DIR/single_match.rs:140:5
|
LL | / match x {
LL | | Bar::A => println!(),
@@ -120,7 +120,7 @@ LL | | }
| |_____^ help: try this: `if let Bar::A = x { println!() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:143:5
+ --> $DIR/single_match.rs:148:5
|
LL | / match x {
LL | | None => println!(),
@@ -129,7 +129,7 @@ LL | | };
| |_____^ help: try this: `if let None = x { println!() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:165:5
+ --> $DIR/single_match.rs:170:5
|
LL | / match x {
LL | | (Some(_), _) => {},
@@ -138,7 +138,7 @@ LL | | }
| |_____^ help: try this: `if let (Some(_), _) = x {}`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:171:5
+ --> $DIR/single_match.rs:176:5
|
LL | / match x {
LL | | (Some(E::V), _) => todo!(),
@@ -147,7 +147,7 @@ LL | | }
| |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:177:5
+ --> $DIR/single_match.rs:182:5
|
LL | / match (Some(42), Some(E::V), Some(42)) {
LL | | (.., Some(E::V), _) => {},
@@ -155,5 +155,47 @@ LL | | (..) => {},
LL | | }
| |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}`
-error: aborting due to 16 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:254:5
+ |
+LL | / match bar {
+LL | | Some(v) => unsafe {
+LL | | let r = &v as *const i32;
+LL | | println!("{}", *r);
+LL | | },
+LL | | _ => {},
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar { unsafe {
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + } }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:262:5
+ |
+LL | / match bar {
+LL | | #[rustfmt::skip]
+LL | | Some(v) => {
+LL | | unsafe {
+... |
+LL | | _ => {},
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar {
+LL + unsafe {
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + }
+LL + }
+ |
+
+error: aborting due to 18 previous errors
diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed
new file mode 100644
index 000000000..fcc8f1480
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match_else.fixed
@@ -0,0 +1,173 @@
+//@run-rustfix
+//@aux-build: proc_macros.rs:proc-macro
+#![warn(clippy::single_match_else)]
+#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
+extern crate proc_macros;
+use proc_macros::with_span;
+
+enum ExprNode {
+ ExprAddrOf,
+ Butterflies,
+ Unicorns,
+}
+
+static NODE: ExprNode = ExprNode::Unicorns;
+
+fn unwrap_addr() -> Option<&'static ExprNode> {
+ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
+ let x = 5;
+ None
+ };
+
+ // Don't lint
+ with_span!(span match ExprNode::Butterflies {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ })
+}
+
+macro_rules! unwrap_addr {
+ ($expression:expr) => {
+ match $expression {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ }
+ };
+}
+
+#[rustfmt::skip]
+fn main() {
+ unwrap_addr!(ExprNode::Unicorns);
+
+ //
+ // don't lint single exprs/statements
+ //
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => return,
+ }
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ return
+ },
+ }
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ return;
+ },
+ }
+
+ //
+ // lint multiple exprs/statements "else" blocks
+ //
+
+ // lint here
+ if let Some(a) = Some(1) { println!("${:?}", a) } else {
+ println!("else block");
+ return
+ }
+
+ // lint here
+ if let Some(a) = Some(1) { println!("${:?}", a) } else {
+ println!("else block");
+ return;
+ }
+
+ // lint here
+ use std::convert::Infallible;
+ if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
+ println!("else block");
+ return;
+ }
+
+ use std::borrow::Cow;
+ if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else {
+ println!("else block");
+ return;
+ }
+}
+
+fn issue_10808(bar: Option<i32>) {
+ if let Some(v) = bar { unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ } } else {
+ println!("None1");
+ println!("None2");
+ }
+
+ if let Some(v) = bar {
+ println!("Some");
+ println!("{v}");
+ } else { unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ } }
+
+ if let Some(v) = bar { unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ } } else { unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ } }
+
+ if let Some(v) = bar {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ } else {
+ println!("None");
+ println!("None");
+ }
+
+ match bar {
+ Some(v) => {
+ println!("Some");
+ println!("{v}");
+ },
+ #[rustfmt::skip]
+ None => {
+ unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ }
+
+ match bar {
+ #[rustfmt::skip]
+ Some(v) => {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ #[rustfmt::skip]
+ None => {
+ unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
index c8ac768b6..77afd58a0 100644
--- a/src/tools/clippy/tests/ui/single_match_else.rs
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -1,7 +1,7 @@
-//@aux-build: proc_macros.rs
+//@run-rustfix
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::single_match_else)]
-#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
-
+#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
extern crate proc_macros;
use proc_macros::with_span;
@@ -115,3 +115,87 @@ fn main() {
}
}
}
+
+fn issue_10808(bar: Option<i32>) {
+ match bar {
+ Some(v) => unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ },
+ None => {
+ println!("None1");
+ println!("None2");
+ },
+ }
+
+ match bar {
+ Some(v) => {
+ println!("Some");
+ println!("{v}");
+ },
+ None => unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ },
+ }
+
+ match bar {
+ Some(v) => unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ },
+ None => unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ },
+ }
+
+ match bar {
+ #[rustfmt::skip]
+ Some(v) => {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ None => {
+ println!("None");
+ println!("None");
+ },
+ }
+
+ match bar {
+ Some(v) => {
+ println!("Some");
+ println!("{v}");
+ },
+ #[rustfmt::skip]
+ None => {
+ unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ }
+
+ match bar {
+ #[rustfmt::skip]
+ Some(v) => {
+ unsafe {
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ #[rustfmt::skip]
+ None => {
+ unsafe {
+ let v = 0;
+ let r = &v as *const i32;
+ println!("{}", *r);
+ }
+ },
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr
index 62876a55d..228236f3b 100644
--- a/src/tools/clippy/tests/ui/single_match_else.stderr
+++ b/src/tools/clippy/tests/ui/single_match_else.stderr
@@ -100,5 +100,101 @@ LL + return;
LL + }
|
-error: aborting due to 5 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:120:5
+ |
+LL | / match bar {
+LL | | Some(v) => unsafe {
+LL | | let r = &v as *const i32;
+LL | | println!("{}", *r);
+... |
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar { unsafe {
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + } } else {
+LL + println!("None1");
+LL + println!("None2");
+LL + }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:131:5
+ |
+LL | / match bar {
+LL | | Some(v) => {
+LL | | println!("Some");
+LL | | println!("{v}");
+... |
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar {
+LL + println!("Some");
+LL + println!("{v}");
+LL + } else { unsafe {
+LL + let v = 0;
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + } }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:143:5
+ |
+LL | / match bar {
+LL | | Some(v) => unsafe {
+LL | | let r = &v as *const i32;
+LL | | println!("{}", *r);
+... |
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar { unsafe {
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + } } else { unsafe {
+LL + let v = 0;
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + } }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:155:5
+ |
+LL | / match bar {
+LL | | #[rustfmt::skip]
+LL | | Some(v) => {
+LL | | unsafe {
+... |
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(v) = bar {
+LL + unsafe {
+LL + let r = &v as *const i32;
+LL + println!("{}", *r);
+LL + }
+LL + } else {
+LL + println!("None");
+LL + println!("None");
+LL + }
+ |
+
+error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs
new file mode 100644
index 000000000..833e1c43b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs
@@ -0,0 +1,58 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(clippy::no_effect, clippy::useless_vec, unused)]
+#![warn(clippy::single_range_in_vec_init)]
+#![feature(generic_arg_infer)]
+
+#[macro_use]
+extern crate proc_macros;
+
+macro_rules! a {
+ () => {
+ vec![0..200];
+ };
+}
+
+fn awa<T: PartialOrd>(start: T, end: T) {
+ [start..end];
+}
+
+fn awa_vec<T: PartialOrd>(start: T, end: T) {
+ vec![start..end];
+}
+
+fn main() {
+ // Lint
+ [0..200];
+ vec![0..200];
+ [0u8..200];
+ [0usize..200];
+ [0..200usize];
+ vec![0u8..200];
+ vec![0usize..200];
+ vec![0..200usize];
+ // Only suggest collect
+ [0..200isize];
+ vec![0..200isize];
+ // Do not lint
+ [0..200, 0..100];
+ vec![0..200, 0..100];
+ [0.0..200.0];
+ vec![0.0..200.0];
+ // `Copy` is not implemented for `Range`, so this doesn't matter
+ // FIXME: [0..200; 2];
+ // FIXME: [vec!0..200; 2];
+
+ // Unfortunately skips any macros
+ a!();
+
+ // Skip external macros and procedural macros
+ external! {
+ [0..200];
+ vec![0..200];
+ }
+ with_span! {
+ span
+ [0..200];
+ vec![0..200];
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr
new file mode 100644
index 000000000..3e3d521f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr
@@ -0,0 +1,145 @@
+error: an array of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:25:5
+ |
+LL | [0..200];
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::single-range-in-vec-init` implied by `-D warnings`
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200).collect::<std::vec::Vec<i32>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted an array of len 200, try
+ |
+LL | [0; 200];
+ | ~~~~~~
+
+error: a `Vec` of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:26:5
+ |
+LL | vec![0..200];
+ | ^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200).collect::<std::vec::Vec<i32>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted a `Vec` of len 200, try
+ |
+LL | vec![0; 200];
+ | ~~~~~~
+
+error: an array of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:27:5
+ |
+LL | [0u8..200];
+ | ^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0u8..200).collect::<std::vec::Vec<u8>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted an array of len 200, try
+ |
+LL | [0u8; 200];
+ | ~~~~~~~~
+
+error: an array of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:28:5
+ |
+LL | [0usize..200];
+ | ^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0usize..200).collect::<std::vec::Vec<usize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted an array of len 200, try
+ |
+LL | [0usize; 200];
+ | ~~~~~~~~~~~
+
+error: an array of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:29:5
+ |
+LL | [0..200usize];
+ | ^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200usize).collect::<std::vec::Vec<usize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted an array of len 200usize, try
+ |
+LL | [0; 200usize];
+ | ~~~~~~~~~~~
+
+error: a `Vec` of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:30:5
+ |
+LL | vec![0u8..200];
+ | ^^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0u8..200).collect::<std::vec::Vec<u8>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted a `Vec` of len 200, try
+ |
+LL | vec![0u8; 200];
+ | ~~~~~~~~
+
+error: a `Vec` of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:31:5
+ |
+LL | vec![0usize..200];
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0usize..200).collect::<std::vec::Vec<usize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted a `Vec` of len 200, try
+ |
+LL | vec![0usize; 200];
+ | ~~~~~~~~~~~
+
+error: a `Vec` of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:32:5
+ |
+LL | vec![0..200usize];
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200usize).collect::<std::vec::Vec<usize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if you wanted a `Vec` of len 200usize, try
+ |
+LL | vec![0; 200usize];
+ | ~~~~~~~~~~~
+
+error: an array of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:34:5
+ |
+LL | [0..200isize];
+ | ^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200isize).collect::<std::vec::Vec<isize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: a `Vec` of `Range` that is only one element
+ --> $DIR/single_range_in_vec_init.rs:35:5
+ |
+LL | vec![0..200isize];
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if you wanted a `Vec` that contains the entire range, try
+ |
+LL | (0..200isize).collect::<std::vec::Vec<isize>>();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/skip_while_next.rs b/src/tools/clippy/tests/ui/skip_while_next.rs
index 62574e2c8..8e4cd82ce 100644
--- a/src/tools/clippy/tests/ui/skip_while_next.rs
+++ b/src/tools/clippy/tests/ui/skip_while_next.rs
@@ -1,7 +1,7 @@
//@aux-build:option_helpers.rs
#![warn(clippy::skip_while_next)]
-#![allow(clippy::disallowed_names)]
+#![allow(clippy::disallowed_names, clippy::useless_vec)]
extern crate option_helpers;
use option_helpers::IteratorFalsePositives;
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
index 1370dd2df..50c1fc71a 100644
--- a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::stable_sort_primitive)]
+#![allow(clippy::useless_vec)]
fn main() {
// positive examples
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.rs b/src/tools/clippy/tests/ui/stable_sort_primitive.rs
index cd344dd12..bd1bb428f 100644
--- a/src/tools/clippy/tests/ui/stable_sort_primitive.rs
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.rs
@@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::stable_sort_primitive)]
+#![allow(clippy::useless_vec)]
fn main() {
// positive examples
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
index 1432fdcff..aa5d7b7e4 100644
--- a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
@@ -1,5 +1,5 @@
error: used `sort` on primitive type `i32`
- --> $DIR/stable_sort_primitive.rs:7:5
+ --> $DIR/stable_sort_primitive.rs:8:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -8,7 +8,7 @@ LL | vec.sort();
= note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
error: used `sort` on primitive type `bool`
- --> $DIR/stable_sort_primitive.rs:9:5
+ --> $DIR/stable_sort_primitive.rs:10:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -16,7 +16,7 @@ LL | vec.sort();
= note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `char`
- --> $DIR/stable_sort_primitive.rs:11:5
+ --> $DIR/stable_sort_primitive.rs:12:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -24,7 +24,7 @@ LL | vec.sort();
= note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `str`
- --> $DIR/stable_sort_primitive.rs:13:5
+ --> $DIR/stable_sort_primitive.rs:14:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -32,7 +32,7 @@ LL | vec.sort();
= note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `tuple`
- --> $DIR/stable_sort_primitive.rs:15:5
+ --> $DIR/stable_sort_primitive.rs:16:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -40,7 +40,7 @@ LL | vec.sort();
= note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `array`
- --> $DIR/stable_sort_primitive.rs:17:5
+ --> $DIR/stable_sort_primitive.rs:18:5
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
@@ -48,7 +48,7 @@ LL | vec.sort();
= note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `i32`
- --> $DIR/stable_sort_primitive.rs:19:5
+ --> $DIR/stable_sort_primitive.rs:20:5
|
LL | arr.sort();
| ^^^^^^^^^^ help: try: `arr.sort_unstable()`
diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed
index 29d56f852..b7237069d 100644
--- a/src/tools/clippy/tests/ui/starts_ends_with.fixed
+++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, unused_must_use)]
+#![allow(clippy::needless_if, dead_code, unused_must_use)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs
index 56bbe2574..658312e87 100644
--- a/src/tools/clippy/tests/ui/starts_ends_with.rs
+++ b/src/tools/clippy/tests/ui/starts_ends_with.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-#![allow(dead_code, unused_must_use)]
+#![allow(clippy::needless_if, dead_code, unused_must_use)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs
index de78dfe4d..6980242ae 100644
--- a/src/tools/clippy/tests/ui/string_add.rs
+++ b/src/tools/clippy/tests/ui/string_add.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
extern crate proc_macros;
use proc_macros::external;
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
index 3fc11b8b0..0edd81acc 100644
--- a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
//@aux-build:macro_rules.rs
-#![allow(dead_code, unused_variables)]
+#![allow(clippy::needless_raw_string_hashes, dead_code, unused_variables)]
#![warn(clippy::string_lit_as_bytes)]
#[macro_use]
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
index 7d54acf63..2647f02f0 100644
--- a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
@@ -1,7 +1,7 @@
//@run-rustfix
//@aux-build:macro_rules.rs
-#![allow(dead_code, unused_variables)]
+#![allow(clippy::needless_raw_string_hashes, dead_code, unused_variables)]
#![warn(clippy::string_lit_as_bytes)]
#[macro_use]
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
index e0153cdd8..0473ccdc3 100644
--- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
@@ -1,7 +1,12 @@
-//@aux-build:proc_macro_suspicious_else_formatting.rs
+//@aux-build:proc_macro_suspicious_else_formatting.rs:proc-macro
#![warn(clippy::suspicious_else_formatting)]
-#![allow(clippy::if_same_then_else, clippy::let_unit_value)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::let_unit_value,
+ clippy::needless_if,
+ clippy::needless_else
+)]
extern crate proc_macro_suspicious_else_formatting;
use proc_macro_suspicious_else_formatting::DeriveBadSpan;
@@ -108,6 +113,13 @@ fn main() {
else
{
}
+
+ //#10273 This is fine. Don't warn
+ if foo() {
+ } else
+ /* whelp */
+ {
+ }
}
// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
index 2e512b47f..723fdd7e9 100644
--- a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
@@ -1,5 +1,5 @@
error: this looks like an `else {..}` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:17:6
+ --> $DIR/suspicious_else_formatting.rs:22:6
|
LL | } {
| ^
@@ -8,7 +8,7 @@ LL | } {
= note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:21:6
+ --> $DIR/suspicious_else_formatting.rs:26:6
|
LL | } if foo() {
| ^
@@ -16,7 +16,7 @@ LL | } if foo() {
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:28:10
+ --> $DIR/suspicious_else_formatting.rs:33:10
|
LL | } if foo() {
| ^
@@ -24,7 +24,7 @@ LL | } if foo() {
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:36:10
+ --> $DIR/suspicious_else_formatting.rs:41:10
|
LL | } if foo() {
| ^
@@ -32,7 +32,7 @@ LL | } if foo() {
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:45:6
+ --> $DIR/suspicious_else_formatting.rs:50:6
|
LL | } else
| ______^
@@ -42,7 +42,7 @@ LL | | {
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:57:6
+ --> $DIR/suspicious_else_formatting.rs:62:6
|
LL | } else
| ______^
@@ -52,7 +52,7 @@ LL | | if foo() { // the span of the above error should continue here
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:62:6
+ --> $DIR/suspicious_else_formatting.rs:67:6
|
LL | }
| ______^
@@ -63,7 +63,7 @@ LL | | if foo() { // the span of the above error should continue here
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:89:6
+ --> $DIR/suspicious_else_formatting.rs:94:6
|
LL | }
| ______^
@@ -75,7 +75,7 @@ LL | | {
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:97:6
+ --> $DIR/suspicious_else_formatting.rs:102:6
|
LL | }
| ______^
diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
index 9564e373c..3c5ca1762 100644
--- a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
+++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
@@ -1,4 +1,5 @@
#![warn(clippy::suspicious_unary_op_formatting)]
+#![allow(clippy::needless_if)]
#[rustfmt::skip]
fn main() {
diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
index 9f1289ccb..52b0e99a1 100644
--- a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
@@ -1,5 +1,5 @@
error: by not having a space between `>` and `-` it looks like `>-` is a single operator
- --> $DIR/suspicious_unary_op_formatting.rs:8:9
+ --> $DIR/suspicious_unary_op_formatting.rs:9:9
|
LL | if a >- 30 {}
| ^^^^
@@ -8,7 +8,7 @@ LL | if a >- 30 {}
= note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings`
error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator
- --> $DIR/suspicious_unary_op_formatting.rs:9:9
+ --> $DIR/suspicious_unary_op_formatting.rs:10:9
|
LL | if a >=- 30 {}
| ^^^^^
@@ -16,7 +16,7 @@ LL | if a >=- 30 {}
= help: put a space between `>=` and `-` and remove the space after `-`
error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator
- --> $DIR/suspicious_unary_op_formatting.rs:14:9
+ --> $DIR/suspicious_unary_op_formatting.rs:15:9
|
LL | if b &&! c {}
| ^^^^^
@@ -24,7 +24,7 @@ LL | if b &&! c {}
= help: put a space between `&&` and `!` and remove the space after `!`
error: by not having a space between `>` and `-` it looks like `>-` is a single operator
- --> $DIR/suspicious_unary_op_formatting.rs:16:9
+ --> $DIR/suspicious_unary_op_formatting.rs:17:9
|
LL | if a >- 30 {}
| ^^^^^^
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
index fd3569cf3..22f904e3f 100644
--- a/src/tools/clippy/tests/ui/swap.fixed
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -10,7 +10,8 @@
dead_code,
unused_assignments,
unused_variables,
- clippy::let_and_return
+ clippy::let_and_return,
+ clippy::useless_vec
)]
struct Foo(u32);
diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs
index 34fbce052..ada64f89e 100644
--- a/src/tools/clippy/tests/ui/swap.rs
+++ b/src/tools/clippy/tests/ui/swap.rs
@@ -10,7 +10,8 @@
dead_code,
unused_assignments,
unused_variables,
- clippy::let_and_return
+ clippy::let_and_return,
+ clippy::useless_vec
)]
struct Foo(u32);
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
index 0c2462684..a3b9c2b74 100644
--- a/src/tools/clippy/tests/ui/swap.stderr
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -1,5 +1,5 @@
error: this looks like you are swapping `bar.a` and `bar.b` manually
- --> $DIR/swap.rs:27:5
+ --> $DIR/swap.rs:28:5
|
LL | / let temp = bar.a;
LL | | bar.a = bar.b;
@@ -10,7 +10,7 @@ LL | | bar.b = temp;
= note: `-D clippy::manual-swap` implied by `-D warnings`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:39:5
+ --> $DIR/swap.rs:40:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
@@ -18,7 +18,7 @@ LL | | foo[1] = temp;
| |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:48:5
+ --> $DIR/swap.rs:49:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
@@ -26,7 +26,7 @@ LL | | foo[1] = temp;
| |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:67:5
+ --> $DIR/swap.rs:68:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
@@ -34,7 +34,7 @@ LL | | foo[1] = temp;
| |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping `a` and `b` manually
- --> $DIR/swap.rs:78:5
+ --> $DIR/swap.rs:79:5
|
LL | / a ^= b;
LL | | b ^= a;
@@ -42,7 +42,7 @@ LL | | a ^= b;
| |___________^ help: try: `std::mem::swap(&mut a, &mut b);`
error: this looks like you are swapping `bar.a` and `bar.b` manually
- --> $DIR/swap.rs:86:5
+ --> $DIR/swap.rs:87:5
|
LL | / bar.a ^= bar.b;
LL | | bar.b ^= bar.a;
@@ -50,7 +50,7 @@ LL | | bar.a ^= bar.b;
| |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:94:5
+ --> $DIR/swap.rs:95:5
|
LL | / foo[0] ^= foo[1];
LL | | foo[1] ^= foo[0];
@@ -58,7 +58,7 @@ LL | | foo[0] ^= foo[1];
| |_____________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
- --> $DIR/swap.rs:123:5
+ --> $DIR/swap.rs:124:5
|
LL | / let temp = foo[0][1];
LL | | foo[0][1] = bar[1][0];
@@ -68,7 +68,7 @@ LL | | bar[1][0] = temp;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `a` and `b` manually
- --> $DIR/swap.rs:137:7
+ --> $DIR/swap.rs:138:7
|
LL | ; let t = a;
| _______^
@@ -79,7 +79,7 @@ LL | | b = t;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `c.0` and `a` manually
- --> $DIR/swap.rs:146:7
+ --> $DIR/swap.rs:147:7
|
LL | ; let t = c.0;
| _______^
@@ -90,7 +90,7 @@ LL | | a = t;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `b` and `a` manually
- --> $DIR/swap.rs:172:5
+ --> $DIR/swap.rs:173:5
|
LL | / let t = b;
LL | | b = a;
@@ -100,7 +100,7 @@ LL | | a = t;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `a` and `b`
- --> $DIR/swap.rs:134:5
+ --> $DIR/swap.rs:135:5
|
LL | / a = b;
LL | | b = a;
@@ -110,7 +110,7 @@ LL | | b = a;
= note: `-D clippy::almost-swapped` implied by `-D warnings`
error: this looks like you are trying to swap `c.0` and `a`
- --> $DIR/swap.rs:143:5
+ --> $DIR/swap.rs:144:5
|
LL | / c.0 = a;
LL | | a = c.0;
@@ -119,7 +119,7 @@ LL | | a = c.0;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `a` and `b`
- --> $DIR/swap.rs:150:5
+ --> $DIR/swap.rs:151:5
|
LL | / let a = b;
LL | | let b = a;
@@ -128,7 +128,7 @@ LL | | let b = a;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `d` and `c`
- --> $DIR/swap.rs:155:5
+ --> $DIR/swap.rs:156:5
|
LL | / d = c;
LL | | c = d;
@@ -137,7 +137,7 @@ LL | | c = d;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `a` and `b`
- --> $DIR/swap.rs:159:5
+ --> $DIR/swap.rs:160:5
|
LL | / let a = b;
LL | | b = a;
@@ -146,7 +146,7 @@ LL | | b = a;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `s.0.x` and `s.0.y` manually
- --> $DIR/swap.rs:207:5
+ --> $DIR/swap.rs:208:5
|
LL | / let t = s.0.x;
LL | | s.0.x = s.0.y;
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.rs b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
index 21fdfdf90..d53c692b7 100644
--- a/src/tools/clippy/tests/ui/tests_outside_test_module.rs
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
@@ -1,4 +1,3 @@
-//@compile-flags: --test
#![allow(unused)]
#![warn(clippy::tests_outside_test_module)]
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.stderr b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
index 125a79d6e..71c649c5d 100644
--- a/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
@@ -1,5 +1,5 @@
error: this function marked with #[test] is outside a #[cfg(test)] module
- --> $DIR/tests_outside_test_module.rs:11:1
+ --> $DIR/tests_outside_test_module.rs:10:1
|
LL | fn my_test() {}
| ^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/to_string_in_format_args_incremental.fixed b/src/tools/clippy/tests/ui/to_string_in_format_args_incremental.fixed
new file mode 100644
index 000000000..9f75ad895
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_string_in_format_args_incremental.fixed
@@ -0,0 +1,9 @@
+//@run-rustfix
+//@compile-flags: -C incremental=target/debug/test/incr
+
+// see https://github.com/rust-lang/rust-clippy/issues/10969
+
+fn main() {
+ let s = "Hello, world!";
+ println!("{}", s);
+}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
index ea30c1fda..9ad45c7a8 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::toplevel_ref_arg)]
-#![allow(clippy::uninlined_format_args, unused)]
+#![allow(clippy::uninlined_format_args, unused, clippy::useless_vec)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
index 7a3d33e5b..45ccc024c 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
@@ -1,7 +1,7 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::toplevel_ref_arg)]
-#![allow(clippy::uninlined_format_args, unused)]
+#![allow(clippy::uninlined_format_args, unused, clippy::useless_vec)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
index 8aaf47b1b..464762af8 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::toplevel_ref_arg)]
#![allow(unused)]
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
index f06ffab5d..61a6c98ed 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
@@ -32,7 +32,7 @@ fn transmute_ptr_to_ptr() {
// ref-ref transmutes; bad
let _: &f32 = std::mem::transmute(&1u32);
let _: &f64 = std::mem::transmute(&1f32);
- // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
+ //:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
// the same type
let _: &mut f32 = std::mem::transmute(&mut 1u32);
let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
index 575dadde9..215f0ac18 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::transmute_ptr_to_ref)]
-#![allow(clippy::match_single_binding)]
+#![allow(clippy::match_single_binding, clippy::unnecessary_cast)]
unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
let _: &T = &*p;
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
index 4238ff804..3528e1379 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![warn(clippy::transmute_ptr_to_ref)]
-#![allow(clippy::match_single_binding)]
+#![allow(clippy::match_single_binding, clippy::unnecessary_cast)]
unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
let _: &T = std::mem::transmute(p);
diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed
index dc773ad4b..181674087 100644
--- a/src/tools/clippy/tests/ui/try_err.fixed
+++ b/src/tools/clippy/tests/ui/try_err.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs
index 7a7433a7e..0e47c4d02 100644
--- a/src/tools/clippy/tests/ui/try_err.rs
+++ b/src/tools/clippy/tests/ui/try_err.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
diff --git a/src/tools/clippy/tests/ui/tuple_array_conversions.rs b/src/tools/clippy/tests/ui/tuple_array_conversions.rs
new file mode 100644
index 000000000..f96a7c97f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tuple_array_conversions.rs
@@ -0,0 +1,73 @@
+//@aux-build:proc_macros.rs:proc-macro
+#![allow(clippy::no_effect, clippy::useless_vec, unused)]
+#![warn(clippy::tuple_array_conversions)]
+
+#[macro_use]
+extern crate proc_macros;
+
+fn main() {
+ let x = [1, 2];
+ let x = (x[0], x[1]);
+ let x = [x.0, x.1];
+ let x = &[1, 2];
+ let x = (x[0], x[1]);
+
+ let t1: &[(u32, u32)] = &[(1, 2), (3, 4)];
+ let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();
+ t1.iter().for_each(|&(a, b)| _ = [a, b]);
+ let t2: Vec<(u32, u32)> = v1.iter().map(|&[a, b]| (a, b)).collect();
+ t1.iter().for_each(|&(a, b)| _ = [a, b]);
+ // Do not lint
+ let v2: Vec<[u32; 2]> = t1.iter().map(|&t| t.into()).collect();
+ let t3: Vec<(u32, u32)> = v2.iter().map(|&v| v.into()).collect();
+ let x = [1; 13];
+ let x = (
+ x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12],
+ );
+ let x = [x.0, x.1, x.2, x.3, x.4, x.5, x.6, x.7, x.8, x.9, x.10, x.11, x.12];
+ let x = (1, 2);
+ let x = (x.0, x.1);
+ let x = [1, 2];
+ let x = [x[0], x[1]];
+ let x = vec![1, 2];
+ let x = (x[0], x[1]);
+ let x = [1; 3];
+ let x = (x[0],);
+ let x = (1, 2, 3);
+ let x = [x.0];
+ let x = (1, 2);
+ let y = (1, 2);
+ [x.0, y.0];
+ [x.0, y.1];
+ let x = [x.0, x.0];
+ let x = (x[0], x[0]);
+ external! {
+ let t1: &[(u32, u32)] = &[(1, 2), (3, 4)];
+ let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();
+ let t2: Vec<(u32, u32)> = v1.iter().map(|&[a, b]| (a, b)).collect();
+ }
+ with_span! {
+ span
+ let t1: &[(u32, u32)] = &[(1, 2), (3, 4)];
+ let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();
+ let t2: Vec<(u32, u32)> = v1.iter().map(|&[a, b]| (a, b)).collect();
+ }
+}
+
+#[clippy::msrv = "1.70.0"]
+fn msrv_too_low() {
+ let x = [1, 2];
+ let x = (x[0], x[1]);
+ let x = [x.0, x.1];
+ let x = &[1, 2];
+ let x = (x[0], x[1]);
+}
+
+#[clippy::msrv = "1.71.0"]
+fn msrv_juust_right() {
+ let x = [1, 2];
+ let x = (x[0], x[1]);
+ let x = [x.0, x.1];
+ let x = &[1, 2];
+ let x = (x[0], x[1]);
+}
diff --git a/src/tools/clippy/tests/ui/tuple_array_conversions.stderr b/src/tools/clippy/tests/ui/tuple_array_conversions.stderr
new file mode 100644
index 000000000..be653e8ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tuple_array_conversions.stderr
@@ -0,0 +1,83 @@
+error: it looks like you're trying to convert an array to a tuple
+ --> $DIR/tuple_array_conversions.rs:10:13
+ |
+LL | let x = (x[0], x[1]);
+ | ^^^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
+ = note: `-D clippy::tuple-array-conversions` implied by `-D warnings`
+
+error: it looks like you're trying to convert a tuple to an array
+ --> $DIR/tuple_array_conversions.rs:11:13
+ |
+LL | let x = [x.0, x.1];
+ | ^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
+
+error: it looks like you're trying to convert an array to a tuple
+ --> $DIR/tuple_array_conversions.rs:13:13
+ |
+LL | let x = (x[0], x[1]);
+ | ^^^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
+
+error: it looks like you're trying to convert a tuple to an array
+ --> $DIR/tuple_array_conversions.rs:16:53
+ |
+LL | let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();
+ | ^^^^^^
+ |
+ = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
+
+error: it looks like you're trying to convert a tuple to an array
+ --> $DIR/tuple_array_conversions.rs:17:38
+ |
+LL | t1.iter().for_each(|&(a, b)| _ = [a, b]);
+ | ^^^^^^
+ |
+ = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
+
+error: it looks like you're trying to convert an array to a tuple
+ --> $DIR/tuple_array_conversions.rs:18:55
+ |
+LL | let t2: Vec<(u32, u32)> = v1.iter().map(|&[a, b]| (a, b)).collect();
+ | ^^^^^^
+ |
+ = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
+
+error: it looks like you're trying to convert a tuple to an array
+ --> $DIR/tuple_array_conversions.rs:19:38
+ |
+LL | t1.iter().for_each(|&(a, b)| _ = [a, b]);
+ | ^^^^^^
+ |
+ = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
+
+error: it looks like you're trying to convert an array to a tuple
+ --> $DIR/tuple_array_conversions.rs:69:13
+ |
+LL | let x = (x[0], x[1]);
+ | ^^^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
+
+error: it looks like you're trying to convert a tuple to an array
+ --> $DIR/tuple_array_conversions.rs:70:13
+ |
+LL | let x = [x.0, x.1];
+ | ^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
+
+error: it looks like you're trying to convert an array to a tuple
+ --> $DIR/tuple_array_conversions.rs:72:13
+ |
+LL | let x = (x[0], x[1]);
+ | ^^^^^^^^^^^^
+ |
+ = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/type_complexity.rs b/src/tools/clippy/tests/ui/type_complexity.rs
index 86a7bd7b6..816950110 100644
--- a/src/tools/clippy/tests/ui/type_complexity.rs
+++ b/src/tools/clippy/tests/ui/type_complexity.rs
@@ -1,5 +1,5 @@
#![warn(clippy::all)]
-#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)]
+#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)]
#![feature(associated_type_defaults)]
type Alias = Vec<Vec<Box<(u32, u32, u32, u32)>>>; // no warning here
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
index 8b4613b3f..874d97f7a 100644
--- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
@@ -1,6 +1,7 @@
#![deny(clippy::type_repetition_in_bounds)]
#![allow(clippy::extra_unused_type_parameters)]
+use serde::Deserialize;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
pub fn foo<T>(_t: T)
@@ -70,6 +71,20 @@ mod issue4326 {
}
}
+// Extern macros shouldn't lint, again (see #10504)
+mod issue10504 {
+ use serde::{Deserialize, Serialize};
+ use std::fmt::Debug;
+ use std::hash::Hash;
+
+ #[derive(Debug, Serialize, Deserialize)]
+ #[serde(bound(
+ serialize = "T: Serialize + Hash + Eq",
+ deserialize = "Box<T>: serde::de::DeserializeOwned + Hash + Eq"
+ ))]
+ struct OpaqueParams<T: ?Sized + Debug>(std::marker::PhantomData<T>);
+}
+
// Issue #7360
struct Foo<T, U>
where
@@ -95,4 +110,28 @@ where
// This should not lint
fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+#[clippy::msrv = "1.14.0"]
+mod issue8772_fail {
+ pub trait Trait<X, Y, Z> {}
+
+ pub fn f<T: ?Sized, U>(arg: usize)
+ where
+ T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+ U: Clone + Sync + 'static,
+ {
+ }
+}
+
+#[clippy::msrv = "1.15.0"]
+mod issue8772_pass {
+ pub trait Trait<X, Y, Z> {}
+
+ pub fn f<T: ?Sized, U>(arg: usize)
+ where
+ T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+ U: Clone + Sync + 'static,
+ {
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
index a90df03c0..54973c5bd 100644
--- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
@@ -1,5 +1,5 @@
error: this type has already been used as a bound predicate
- --> $DIR/type_repetition_in_bounds.rs:9:5
+ --> $DIR/type_repetition_in_bounds.rs:10:5
|
LL | T: Clone,
| ^^^^^^^^
@@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this type has already been used as a bound predicate
- --> $DIR/type_repetition_in_bounds.rs:26:5
+ --> $DIR/type_repetition_in_bounds.rs:27:5
|
LL | Self: Copy + Default + Ord,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord,
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
error: this type has already been used as a bound predicate
- --> $DIR/type_repetition_in_bounds.rs:86:5
+ --> $DIR/type_repetition_in_bounds.rs:101:5
|
LL | T: Clone,
| ^^^^^^^^
@@ -28,12 +28,20 @@ LL | T: Clone,
= help: consider combining the bounds: `T: ?Sized + Clone`
error: this type has already been used as a bound predicate
- --> $DIR/type_repetition_in_bounds.rs:91:5
+ --> $DIR/type_repetition_in_bounds.rs:106:5
|
LL | T: ?Sized,
| ^^^^^^^^^
|
= help: consider combining the bounds: `T: Clone + ?Sized`
-error: aborting due to 4 previous errors
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:131:9
+ |
+LL | T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider combining the bounds: `T: ?Sized + Trait<Option<usize>, Box<[String]>, bool>`
+
+error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
index 229d15085..a9cc42954 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_unsafe.rs
+//@aux-build:proc_macro_unsafe.rs:proc-macro
#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
@@ -509,4 +509,26 @@ fn issue_9142() {
};
}
+pub unsafe fn a_function_with_a_very_long_name_to_break_the_line() -> u32 {
+ 1
+}
+
+pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() -> u32 {
+ 2
+}
+
+fn issue_10832() {
+ // Safety: A safety comment. But it will warn anyways
+ let _some_variable_with_a_very_long_name_to_break_the_line =
+ unsafe { a_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Another safety comment. But it will warn anyways
+ const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+
+ // Safety: Yet another safety comment. But it will warn anyways
+ static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 =
+ unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
index d1c1bb5ff..ee1d3aa28 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
@@ -318,5 +318,29 @@ LL | let bar = unsafe {};
|
= help: consider adding a safety comment on the preceding line
-error: aborting due to 36 previous errors
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:523:9
+ |
+LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:527:9
+ |
+LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:531:9
+ |
+LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: aborting due to 39 previous errors
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.rs b/src/tools/clippy/tests/ui/undropped_manually_drops.rs
deleted file mode 100644
index f4cfc92e1..000000000
--- a/src/tools/clippy/tests/ui/undropped_manually_drops.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-#![warn(clippy::undropped_manually_drops)]
-
-struct S;
-
-fn main() {
- let f = std::mem::drop;
- let g = std::mem::ManuallyDrop::drop;
- let mut manual1 = std::mem::ManuallyDrop::new(S);
- let mut manual2 = std::mem::ManuallyDrop::new(S);
- let mut manual3 = std::mem::ManuallyDrop::new(S);
- let mut manual4 = std::mem::ManuallyDrop::new(S);
-
- // These lines will not drop `S` and should be linted
- drop(std::mem::ManuallyDrop::new(S));
- drop(manual1);
-
- // FIXME: this line is not linted, though it should be
- f(manual2);
-
- // These lines will drop `S` and should be okay.
- unsafe {
- std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
- std::mem::ManuallyDrop::drop(&mut manual3);
- g(&mut manual4);
- }
-}
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
deleted file mode 100644
index 92611a9b7..000000000
--- a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: the inner value of this ManuallyDrop will not be dropped
- --> $DIR/undropped_manually_drops.rs:14:5
- |
-LL | drop(std::mem::ManuallyDrop::new(S));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
- = note: `-D clippy::undropped-manually-drops` implied by `-D warnings`
-
-error: the inner value of this ManuallyDrop will not be dropped
- --> $DIR/undropped_manually_drops.rs:15:5
- |
-LL | drop(manual1);
- | ^^^^^^^^^^^^^
- |
- = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui/unicode.fixed b/src/tools/clippy/tests/ui/unicode.fixed
index 910968afa..032040c48 100644
--- a/src/tools/clippy/tests/ui/unicode.fixed
+++ b/src/tools/clippy/tests/ui/unicode.fixed
@@ -1,5 +1,4 @@
//@run-rustfix
-//@compile-flags: --test
#![allow(dead_code)]
#[warn(clippy::invisible_characters)]
diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs
index bc4b84d34..dd215bc60 100644
--- a/src/tools/clippy/tests/ui/unicode.rs
+++ b/src/tools/clippy/tests/ui/unicode.rs
@@ -1,5 +1,4 @@
//@run-rustfix
-//@compile-flags: --test
#![allow(dead_code)]
#[warn(clippy::invisible_characters)]
diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr
index ea74a8145..21cc22a77 100644
--- a/src/tools/clippy/tests/ui/unicode.stderr
+++ b/src/tools/clippy/tests/ui/unicode.stderr
@@ -1,5 +1,5 @@
error: invisible character detected
- --> $DIR/unicode.rs:7:12
+ --> $DIR/unicode.rs:6:12
|
LL | print!("Here >​< is a ZWS, and ​another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
@@ -7,19 +7,19 @@ LL | print!("Here >​< is a ZWS, and ​another");
= note: `-D clippy::invisible-characters` implied by `-D warnings`
error: invisible character detected
- --> $DIR/unicode.rs:9:12
+ --> $DIR/unicode.rs:8:12
|
LL | print!("Here >­< is a SHY, and ­another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
error: invisible character detected
- --> $DIR/unicode.rs:11:12
+ --> $DIR/unicode.rs:10:12
|
LL | print!("Here >⁠< is a WJ, and ⁠another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
error: non-NFC Unicode sequence detected
- --> $DIR/unicode.rs:17:12
+ --> $DIR/unicode.rs:16:12
|
LL | print!("̀àh?");
| ^^^^^ help: consider replacing the string with: `"̀àh?"`
@@ -27,37 +27,37 @@ LL | print!("̀àh?");
= note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:25:16
+ --> $DIR/unicode.rs:24:16
|
LL | print!("Üben!");
| ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
|
note: the lint level is defined here
- --> $DIR/unicode.rs:22:13
+ --> $DIR/unicode.rs:21:13
|
LL | #![deny(clippy::non_ascii_literal)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:31:36
+ --> $DIR/unicode.rs:30:36
|
LL | const _EMPTY_BLOCK: char = '▱';
| ^^^ help: consider replacing the string with: `'/u{25b1}'`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:32:35
+ --> $DIR/unicode.rs:31:35
|
LL | const _FULL_BLOCK: char = '▰';
| ^^^ help: consider replacing the string with: `'/u{25b0}'`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:52:21
+ --> $DIR/unicode.rs:51:21
|
LL | let _ = "悲しいかな、ここに日本語を書くことはできない。";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"/u{60b2}/u{3057}/u{3044}/u{304b}/u{306a}/u{3001}/u{3053}/u{3053}/u{306b}/u{65e5}/u{672c}/u{8a9e}/u{3092}/u{66f8}/u{304f}/u{3053}/u{3068}/u{306f}/u{3067}/u{304d}/u{306a}/u{3044}/u{3002}"`
|
note: the lint level is defined here
- --> $DIR/unicode.rs:41:17
+ --> $DIR/unicode.rs:40:17
|
LL | #![deny(clippy::non_ascii_literal)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
index e25d123dd..a042731a9 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
@@ -1,8 +1,13 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
//@run-rustfix
#![warn(clippy::uninlined_format_args)]
#![allow(named_arguments_used_positionally, unused)]
-#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
+#![allow(
+ clippy::eq_op,
+ clippy::format_in_format_args,
+ clippy::print_literal,
+ clippy::unnecessary_literal_unwrap
+)]
extern crate proc_macros;
use proc_macros::with_span;
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs
index 6793ec244..d830b74d6 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs
@@ -1,8 +1,13 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
//@run-rustfix
#![warn(clippy::uninlined_format_args)]
#![allow(named_arguments_used_positionally, unused)]
-#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
+#![allow(
+ clippy::eq_op,
+ clippy::format_in_format_args,
+ clippy::print_literal,
+ clippy::unnecessary_literal_unwrap
+)]
extern crate proc_macros;
use proc_macros::with_span;
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.stderr b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
index dc4af6ef4..44ca61f00 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
@@ -1,5 +1,5 @@
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:40:5
+ --> $DIR/uninlined_format_args.rs:45:5
|
LL | println!("val='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL + println!("val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:41:5
+ --> $DIR/uninlined_format_args.rs:46:5
|
LL | println!("val='{ }'", local_i32); // 3 spaces
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL + println!("val='{local_i32}'"); // 3 spaces
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:42:5
+ --> $DIR/uninlined_format_args.rs:47:5
|
LL | println!("val='{ }'", local_i32); // tab
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL + println!("val='{local_i32}'"); // tab
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:43:5
+ --> $DIR/uninlined_format_args.rs:48:5
|
LL | println!("val='{ }'", local_i32); // space+tab
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL + println!("val='{local_i32}'"); // space+tab
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:44:5
+ --> $DIR/uninlined_format_args.rs:49:5
|
LL | println!("val='{ }'", local_i32); // tab+space
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL + println!("val='{local_i32}'"); // tab+space
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:45:5
+ --> $DIR/uninlined_format_args.rs:50:5
|
LL | / println!(
LL | | "val='{
@@ -70,7 +70,7 @@ LL | | );
| |_____^
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:50:5
+ --> $DIR/uninlined_format_args.rs:55:5
|
LL | println!("{}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,7 +82,7 @@ LL + println!("{local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:51:5
+ --> $DIR/uninlined_format_args.rs:56:5
|
LL | println!("{}", fn_arg);
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -94,7 +94,7 @@ LL + println!("{fn_arg}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:52:5
+ --> $DIR/uninlined_format_args.rs:57:5
|
LL | println!("{:?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -106,7 +106,7 @@ LL + println!("{local_i32:?}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:53:5
+ --> $DIR/uninlined_format_args.rs:58:5
|
LL | println!("{:#?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,7 +118,7 @@ LL + println!("{local_i32:#?}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:54:5
+ --> $DIR/uninlined_format_args.rs:59:5
|
LL | println!("{:4}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -130,7 +130,7 @@ LL + println!("{local_i32:4}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:55:5
+ --> $DIR/uninlined_format_args.rs:60:5
|
LL | println!("{:04}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -142,7 +142,7 @@ LL + println!("{local_i32:04}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:56:5
+ --> $DIR/uninlined_format_args.rs:61:5
|
LL | println!("{:<3}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL + println!("{local_i32:<3}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:57:5
+ --> $DIR/uninlined_format_args.rs:62:5
|
LL | println!("{:#010x}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -166,7 +166,7 @@ LL + println!("{local_i32:#010x}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:58:5
+ --> $DIR/uninlined_format_args.rs:63:5
|
LL | println!("{:.1}", local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -178,7 +178,7 @@ LL + println!("{local_f64:.1}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:62:5
+ --> $DIR/uninlined_format_args.rs:67:5
|
LL | println!("{} {}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -190,7 +190,7 @@ LL + println!("{local_i32} {local_f64}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:64:5
+ --> $DIR/uninlined_format_args.rs:69:5
|
LL | println!("{}", val);
| ^^^^^^^^^^^^^^^^^^^
@@ -202,7 +202,7 @@ LL + println!("{val}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:65:5
+ --> $DIR/uninlined_format_args.rs:70:5
|
LL | println!("{}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -214,7 +214,7 @@ LL + println!("{val}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:67:5
+ --> $DIR/uninlined_format_args.rs:72:5
|
LL | println!("val='{/t }'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -226,7 +226,7 @@ LL + println!("val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:68:5
+ --> $DIR/uninlined_format_args.rs:73:5
|
LL | println!("val='{/n }'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -238,7 +238,7 @@ LL + println!("val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:69:5
+ --> $DIR/uninlined_format_args.rs:74:5
|
LL | println!("val='{local_i32}'", local_i32 = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -250,7 +250,7 @@ LL + println!("val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:70:5
+ --> $DIR/uninlined_format_args.rs:75:5
|
LL | println!("val='{local_i32}'", local_i32 = fn_arg);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -262,7 +262,7 @@ LL + println!("val='{fn_arg}'");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:71:5
+ --> $DIR/uninlined_format_args.rs:76:5
|
LL | println!("{0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -274,7 +274,7 @@ LL + println!("{local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:72:5
+ --> $DIR/uninlined_format_args.rs:77:5
|
LL | println!("{0:?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -286,7 +286,7 @@ LL + println!("{local_i32:?}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:73:5
+ --> $DIR/uninlined_format_args.rs:78:5
|
LL | println!("{0:#?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -298,7 +298,7 @@ LL + println!("{local_i32:#?}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:74:5
+ --> $DIR/uninlined_format_args.rs:79:5
|
LL | println!("{0:04}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -310,7 +310,7 @@ LL + println!("{local_i32:04}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:75:5
+ --> $DIR/uninlined_format_args.rs:80:5
|
LL | println!("{0:<3}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -322,7 +322,7 @@ LL + println!("{local_i32:<3}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:76:5
+ --> $DIR/uninlined_format_args.rs:81:5
|
LL | println!("{0:#010x}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -334,7 +334,7 @@ LL + println!("{local_i32:#010x}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:77:5
+ --> $DIR/uninlined_format_args.rs:82:5
|
LL | println!("{0:.1}", local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -346,7 +346,7 @@ LL + println!("{local_f64:.1}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:78:5
+ --> $DIR/uninlined_format_args.rs:83:5
|
LL | println!("{0} {0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -358,7 +358,7 @@ LL + println!("{local_i32} {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:79:5
+ --> $DIR/uninlined_format_args.rs:84:5
|
LL | println!("{1} {} {0} {}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -370,7 +370,7 @@ LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:80:5
+ --> $DIR/uninlined_format_args.rs:85:5
|
LL | println!("{0} {1}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -382,7 +382,7 @@ LL + println!("{local_i32} {local_f64}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:81:5
+ --> $DIR/uninlined_format_args.rs:86:5
|
LL | println!("{1} {0}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -394,7 +394,7 @@ LL + println!("{local_f64} {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:82:5
+ --> $DIR/uninlined_format_args.rs:87:5
|
LL | println!("{1} {0} {1} {0}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -406,7 +406,7 @@ LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:84:5
+ --> $DIR/uninlined_format_args.rs:89:5
|
LL | println!("{v}", v = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -418,7 +418,7 @@ LL + println!("{local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:85:5
+ --> $DIR/uninlined_format_args.rs:90:5
|
LL | println!("{local_i32:0$}", width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -430,7 +430,7 @@ LL + println!("{local_i32:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:86:5
+ --> $DIR/uninlined_format_args.rs:91:5
|
LL | println!("{local_i32:w$}", w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -442,7 +442,7 @@ LL + println!("{local_i32:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:87:5
+ --> $DIR/uninlined_format_args.rs:92:5
|
LL | println!("{local_i32:.0$}", prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -454,7 +454,7 @@ LL + println!("{local_i32:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:88:5
+ --> $DIR/uninlined_format_args.rs:93:5
|
LL | println!("{local_i32:.p$}", p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -466,7 +466,7 @@ LL + println!("{local_i32:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:89:5
+ --> $DIR/uninlined_format_args.rs:94:5
|
LL | println!("{:0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -478,7 +478,7 @@ LL + println!("{val:val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:90:5
+ --> $DIR/uninlined_format_args.rs:95:5
|
LL | println!("{0:0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -490,7 +490,7 @@ LL + println!("{val:val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:91:5
+ --> $DIR/uninlined_format_args.rs:96:5
|
LL | println!("{:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -502,7 +502,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:92:5
+ --> $DIR/uninlined_format_args.rs:97:5
|
LL | println!("{0:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -514,7 +514,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:93:5
+ --> $DIR/uninlined_format_args.rs:98:5
|
LL | println!("{0:0$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -526,7 +526,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:94:5
+ --> $DIR/uninlined_format_args.rs:99:5
|
LL | println!("{0:v$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -538,7 +538,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:95:5
+ --> $DIR/uninlined_format_args.rs:100:5
|
LL | println!("{v:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -550,7 +550,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:96:5
+ --> $DIR/uninlined_format_args.rs:101:5
|
LL | println!("{v:v$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -562,7 +562,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:97:5
+ --> $DIR/uninlined_format_args.rs:102:5
|
LL | println!("{v:0$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -574,7 +574,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:98:5
+ --> $DIR/uninlined_format_args.rs:103:5
|
LL | println!("{v:v$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -586,7 +586,7 @@ LL + println!("{val:val$.val$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:99:5
+ --> $DIR/uninlined_format_args.rs:104:5
|
LL | println!("{:0$}", width);
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -598,7 +598,7 @@ LL + println!("{width:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:100:5
+ --> $DIR/uninlined_format_args.rs:105:5
|
LL | println!("{:1$}", local_i32, width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -610,7 +610,7 @@ LL + println!("{local_i32:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:101:5
+ --> $DIR/uninlined_format_args.rs:106:5
|
LL | println!("{:w$}", w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -622,7 +622,7 @@ LL + println!("{width:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:102:5
+ --> $DIR/uninlined_format_args.rs:107:5
|
LL | println!("{:w$}", local_i32, w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -634,7 +634,7 @@ LL + println!("{local_i32:width$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:103:5
+ --> $DIR/uninlined_format_args.rs:108:5
|
LL | println!("{:.0$}", prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -646,7 +646,7 @@ LL + println!("{prec:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:104:5
+ --> $DIR/uninlined_format_args.rs:109:5
|
LL | println!("{:.1$}", local_i32, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -658,7 +658,7 @@ LL + println!("{local_i32:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:105:5
+ --> $DIR/uninlined_format_args.rs:110:5
|
LL | println!("{:.p$}", p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -670,7 +670,7 @@ LL + println!("{prec:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:106:5
+ --> $DIR/uninlined_format_args.rs:111:5
|
LL | println!("{:.p$}", local_i32, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -682,7 +682,7 @@ LL + println!("{local_i32:.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:107:5
+ --> $DIR/uninlined_format_args.rs:112:5
|
LL | println!("{:0$.1$}", width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -694,7 +694,7 @@ LL + println!("{width:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:108:5
+ --> $DIR/uninlined_format_args.rs:113:5
|
LL | println!("{:0$.w$}", width, w = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -706,7 +706,7 @@ LL + println!("{width:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:109:5
+ --> $DIR/uninlined_format_args.rs:114:5
|
LL | println!("{:1$.2$}", local_f64, width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -718,7 +718,7 @@ LL + println!("{local_f64:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:110:5
+ --> $DIR/uninlined_format_args.rs:115:5
|
LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -730,7 +730,7 @@ LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:111:5
+ --> $DIR/uninlined_format_args.rs:116:5
|
LL | / println!(
LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
@@ -739,7 +739,7 @@ LL | | );
| |_____^
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:122:5
+ --> $DIR/uninlined_format_args.rs:127:5
|
LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -751,7 +751,7 @@ LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:123:5
+ --> $DIR/uninlined_format_args.rs:128:5
|
LL | println!("{:w$.p$}", local_i32, w = width, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -763,7 +763,7 @@ LL + println!("{local_i32:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:124:5
+ --> $DIR/uninlined_format_args.rs:129:5
|
LL | println!("{:w$.p$}", w = width, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -775,7 +775,7 @@ LL + println!("{width:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:143:5
+ --> $DIR/uninlined_format_args.rs:148:5
|
LL | / println!(
LL | | "{}",
@@ -785,7 +785,7 @@ LL | | );
| |_____^
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:148:5
+ --> $DIR/uninlined_format_args.rs:153:5
|
LL | println!("{}", /* comment with a comma , in it */ val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -797,7 +797,7 @@ LL + println!("{val}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:154:9
+ --> $DIR/uninlined_format_args.rs:159:9
|
LL | panic!("p1 {}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -809,7 +809,7 @@ LL + panic!("p1 {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:157:9
+ --> $DIR/uninlined_format_args.rs:162:9
|
LL | panic!("p2 {0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -821,7 +821,7 @@ LL + panic!("p2 {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:160:9
+ --> $DIR/uninlined_format_args.rs:165:9
|
LL | panic!("p3 {local_i32}", local_i32 = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -833,7 +833,7 @@ LL + panic!("p3 {local_i32}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:180:5
+ --> $DIR/uninlined_format_args.rs:185:5
|
LL | println!("expand='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs
index d082063c8..fded8db5d 100644
--- a/src/tools/clippy/tests/ui/unit_arg.rs
+++ b/src/tools/clippy/tests/ui/unit_arg.rs
@@ -1,4 +1,4 @@
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::unit_arg)]
#![allow(unused_must_use, unused_variables)]
#![allow(
diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs
index 3d2711043..fc75f548a 100644
--- a/src/tools/clippy/tests/ui/unit_cmp.rs
+++ b/src/tools/clippy/tests/ui/unit_cmp.rs
@@ -2,7 +2,8 @@
#![allow(
clippy::no_effect,
clippy::unnecessary_operation,
- clippy::derive_partial_eq_without_eq
+ clippy::derive_partial_eq_without_eq,
+ clippy::needless_if
)]
#[derive(PartialEq)]
diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr
index 41cf19ae6..79c890d64 100644
--- a/src/tools/clippy/tests/ui/unit_cmp.stderr
+++ b/src/tools/clippy/tests/ui/unit_cmp.stderr
@@ -1,5 +1,5 @@
error: ==-comparison of unit values detected. This will always be true
- --> $DIR/unit_cmp.rs:16:8
+ --> $DIR/unit_cmp.rs:17:8
|
LL | if {
| ________^
@@ -12,7 +12,7 @@ LL | | } {}
= note: `-D clippy::unit-cmp` implied by `-D warnings`
error: >-comparison of unit values detected. This will always be false
- --> $DIR/unit_cmp.rs:22:8
+ --> $DIR/unit_cmp.rs:23:8
|
LL | if {
| ________^
@@ -23,7 +23,7 @@ LL | | } {}
| |_____^
error: `assert_eq` of unit values detected. This will always succeed
- --> $DIR/unit_cmp.rs:28:5
+ --> $DIR/unit_cmp.rs:29:5
|
LL | / assert_eq!(
LL | | {
@@ -35,7 +35,7 @@ LL | | );
| |_____^
error: `debug_assert_eq` of unit values detected. This will always succeed
- --> $DIR/unit_cmp.rs:36:5
+ --> $DIR/unit_cmp.rs:37:5
|
LL | / debug_assert_eq!(
LL | | {
@@ -47,7 +47,7 @@ LL | | );
| |_____^
error: `assert_ne` of unit values detected. This will always fail
- --> $DIR/unit_cmp.rs:45:5
+ --> $DIR/unit_cmp.rs:46:5
|
LL | / assert_ne!(
LL | | {
@@ -59,7 +59,7 @@ LL | | );
| |_____^
error: `debug_assert_ne` of unit values detected. This will always fail
- --> $DIR/unit_cmp.rs:53:5
+ --> $DIR/unit_cmp.rs:54:5
|
LL | / debug_assert_ne!(
LL | | {
diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs
index bdb4710cc..f2a9694f9 100644
--- a/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs
@@ -1,6 +1,7 @@
#![warn(clippy::unit_return_expecting_ord)]
#![allow(clippy::needless_return)]
#![allow(clippy::unused_unit)]
+#![allow(clippy::useless_vec)]
#![feature(is_sorted)]
struct Struct {
diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr
index 1d9564ce2..3a295af55 100644
--- a/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr
+++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr
@@ -1,36 +1,36 @@
error: this closure returns the unit type which also implements Ord
- --> $DIR/unit_return_expecting_ord.rs:18:25
+ --> $DIR/unit_return_expecting_ord.rs:19:25
|
LL | structs.sort_by_key(|s| {
| ^^^
|
help: probably caused by this trailing semicolon
- --> $DIR/unit_return_expecting_ord.rs:19:24
+ --> $DIR/unit_return_expecting_ord.rs:20:24
|
LL | double(s.field);
| ^
= note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings`
error: this closure returns the unit type which also implements PartialOrd
- --> $DIR/unit_return_expecting_ord.rs:22:30
+ --> $DIR/unit_return_expecting_ord.rs:23:30
|
LL | structs.is_sorted_by_key(|s| {
| ^^^
|
help: probably caused by this trailing semicolon
- --> $DIR/unit_return_expecting_ord.rs:23:24
+ --> $DIR/unit_return_expecting_ord.rs:24:24
|
LL | double(s.field);
| ^
error: this closure returns the unit type which also implements PartialOrd
- --> $DIR/unit_return_expecting_ord.rs:25:30
+ --> $DIR/unit_return_expecting_ord.rs:26:30
|
LL | structs.is_sorted_by_key(|s| {
| ^^^
error: this closure returns the unit type which also implements Ord
- --> $DIR/unit_return_expecting_ord.rs:35:25
+ --> $DIR/unit_return_expecting_ord.rs:36:25
|
LL | structs.sort_by_key(|s| unit(s.field));
| ^^^
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
index 49c0e4dc7..debc7e152 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
@@ -3,7 +3,7 @@
#![warn(clippy::pedantic)]
// Should suggest lowercase
#![allow(clippy::all)]
-#![warn(clippy::cmp_nan)]
+#![warn(clippy::cmp_owned)]
// Should suggest similar clippy lint name
#[warn(clippy::if_not_else)]
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
index b60042923..16140fd10 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
@@ -3,7 +3,7 @@
#![warn(clippy::pedantic)]
// Should suggest lowercase
#![allow(clippy::All)]
-#![warn(clippy::CMP_NAN)]
+#![warn(clippy::CMP_OWNED)]
// Should suggest similar clippy lint name
#[warn(clippy::if_not_els)]
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
index 584c42893..880673eef 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
@@ -6,11 +6,11 @@ LL | #![allow(clippy::All)]
|
= note: `-D unknown-lints` implied by `-D warnings`
-error: unknown lint: `clippy::CMP_NAN`
+error: unknown lint: `clippy::CMP_OWNED`
--> $DIR/unknown_clippy_lints.rs:6:9
|
-LL | #![warn(clippy::CMP_NAN)]
- | ^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_nan`
+LL | #![warn(clippy::CMP_OWNED)]
+ | ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned`
error: unknown lint: `clippy::if_not_els`
--> $DIR/unknown_clippy_lints.rs:9:8
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
index bcc231ea7..8efd44baf 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
@@ -1,13 +1,43 @@
//@run-rustfix
+//@aux-build:extern_fake_libc.rs
#![warn(clippy::unnecessary_cast)]
#![allow(
- unused_must_use,
clippy::borrow_as_ptr,
clippy::no_effect,
clippy::nonstandard_macro_braces,
- clippy::unnecessary_operation
+ clippy::unnecessary_operation,
+ nonstandard_style,
+ unused
)]
+extern crate extern_fake_libc;
+
+type PtrConstU8 = *const u8;
+type PtrMutU8 = *mut u8;
+
+fn owo<T>(ptr: *const T) -> *const T {
+ ptr
+}
+
+fn uwu<T, U>(ptr: *const T) -> *const U {
+ ptr as *const U
+}
+
+mod fake_libc {
+ type pid_t = i32;
+ pub unsafe fn getpid() -> pid_t {
+ pid_t::from(0)
+ }
+ // Make sure a where clause does not break it
+ pub fn getpid_SAFE_TRUTH<T: Clone>(t: &T) -> pid_t
+ where
+ T: Clone,
+ {
+ t;
+ unsafe { getpid() }
+ }
+}
+
#[rustfmt::skip]
fn main() {
// Test cast_unnecessary
@@ -22,6 +52,26 @@ fn main() {
1_i32;
1_f32;
+ let _: *mut u8 = [1u8, 2].as_ptr() as *mut u8;
+
+ [1u8, 2].as_ptr();
+ [1u8, 2].as_ptr() as *mut u8;
+ [1u8, 2].as_mut_ptr();
+ [1u8, 2].as_mut_ptr() as *const u8;
+ [1u8, 2].as_ptr() as PtrConstU8;
+ [1u8, 2].as_ptr() as PtrMutU8;
+ [1u8, 2].as_mut_ptr() as PtrMutU8;
+ [1u8, 2].as_mut_ptr() as PtrConstU8;
+ let _: *const u8 = [1u8, 2].as_ptr() as _;
+ let _: *mut u8 = [1u8, 2].as_mut_ptr() as _;
+ let _: *const u8 = [1u8, 2].as_ptr() as *const _;
+ let _: *mut u8 = [1u8, 2].as_mut_ptr() as *mut _;
+
+ owo::<u32>([1u32].as_ptr());
+ uwu::<u32, u8>([1u32].as_ptr());
+ // this will not lint in the function body even though they have the same type, instead here
+ uwu::<u32, u32>([1u32].as_ptr());
+
// macro version
macro_rules! foo {
($a:ident, $b:ident) => {
@@ -35,12 +85,37 @@ fn main() {
foo!(b, f32);
foo!(c, f64);
+ // do not lint cast from cfg-dependant type
+ let x = 0 as std::ffi::c_ulong;
+ let y = x as u64;
+ let x: std::ffi::c_ulong = 0;
+ let y = x as u64;
+
// do not lint cast to cfg-dependant type
- 1 as std::os::raw::c_char;
+ let x = 1 as std::os::raw::c_char;
+ let y = x as u64;
// do not lint cast to alias type
1 as I32Alias;
&1 as &I32Alias;
+ // or from
+ let x: I32Alias = 1;
+ let y = x as u64;
+ fake_libc::getpid_SAFE_TRUTH(&0u32) as i32;
+ extern_fake_libc::getpid_SAFE_TRUTH() as i32;
+ let pid = unsafe { fake_libc::getpid() };
+ pid as i32;
+
+ let i8_ptr: *const i8 = &1;
+ let u8_ptr: *const u8 = &1;
+
+ // cfg dependant pointees
+ i8_ptr as *const std::os::raw::c_char;
+ u8_ptr as *const std::os::raw::c_char;
+
+ // type aliased pointees
+ i8_ptr as *const std::ffi::c_char;
+ u8_ptr as *const std::ffi::c_char;
// issue #9960
macro_rules! bind_var {
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs
index 282b2f128..c7723ef51 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs
@@ -1,13 +1,43 @@
//@run-rustfix
+//@aux-build:extern_fake_libc.rs
#![warn(clippy::unnecessary_cast)]
#![allow(
- unused_must_use,
clippy::borrow_as_ptr,
clippy::no_effect,
clippy::nonstandard_macro_braces,
- clippy::unnecessary_operation
+ clippy::unnecessary_operation,
+ nonstandard_style,
+ unused
)]
+extern crate extern_fake_libc;
+
+type PtrConstU8 = *const u8;
+type PtrMutU8 = *mut u8;
+
+fn owo<T>(ptr: *const T) -> *const T {
+ ptr as *const T
+}
+
+fn uwu<T, U>(ptr: *const T) -> *const U {
+ ptr as *const U
+}
+
+mod fake_libc {
+ type pid_t = i32;
+ pub unsafe fn getpid() -> pid_t {
+ pid_t::from(0)
+ }
+ // Make sure a where clause does not break it
+ pub fn getpid_SAFE_TRUTH<T: Clone>(t: &T) -> pid_t
+ where
+ T: Clone,
+ {
+ t;
+ unsafe { getpid() }
+ }
+}
+
#[rustfmt::skip]
fn main() {
// Test cast_unnecessary
@@ -22,6 +52,26 @@ fn main() {
1_i32 as i32;
1_f32 as f32;
+ let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8;
+
+ [1u8, 2].as_ptr() as *const u8;
+ [1u8, 2].as_ptr() as *mut u8;
+ [1u8, 2].as_mut_ptr() as *mut u8;
+ [1u8, 2].as_mut_ptr() as *const u8;
+ [1u8, 2].as_ptr() as PtrConstU8;
+ [1u8, 2].as_ptr() as PtrMutU8;
+ [1u8, 2].as_mut_ptr() as PtrMutU8;
+ [1u8, 2].as_mut_ptr() as PtrConstU8;
+ let _: *const u8 = [1u8, 2].as_ptr() as _;
+ let _: *mut u8 = [1u8, 2].as_mut_ptr() as _;
+ let _: *const u8 = [1u8, 2].as_ptr() as *const _;
+ let _: *mut u8 = [1u8, 2].as_mut_ptr() as *mut _;
+
+ owo::<u32>([1u32].as_ptr()) as *const u32;
+ uwu::<u32, u8>([1u32].as_ptr()) as *const u8;
+ // this will not lint in the function body even though they have the same type, instead here
+ uwu::<u32, u32>([1u32].as_ptr()) as *const u32;
+
// macro version
macro_rules! foo {
($a:ident, $b:ident) => {
@@ -35,12 +85,37 @@ fn main() {
foo!(b, f32);
foo!(c, f64);
+ // do not lint cast from cfg-dependant type
+ let x = 0 as std::ffi::c_ulong;
+ let y = x as u64;
+ let x: std::ffi::c_ulong = 0;
+ let y = x as u64;
+
// do not lint cast to cfg-dependant type
- 1 as std::os::raw::c_char;
+ let x = 1 as std::os::raw::c_char;
+ let y = x as u64;
// do not lint cast to alias type
1 as I32Alias;
&1 as &I32Alias;
+ // or from
+ let x: I32Alias = 1;
+ let y = x as u64;
+ fake_libc::getpid_SAFE_TRUTH(&0u32) as i32;
+ extern_fake_libc::getpid_SAFE_TRUTH() as i32;
+ let pid = unsafe { fake_libc::getpid() };
+ pid as i32;
+
+ let i8_ptr: *const i8 = &1;
+ let u8_ptr: *const u8 = &1;
+
+ // cfg dependant pointees
+ i8_ptr as *const std::os::raw::c_char;
+ u8_ptr as *const std::os::raw::c_char;
+
+ // type aliased pointees
+ i8_ptr as *const std::ffi::c_char;
+ u8_ptr as *const std::ffi::c_char;
// issue #9960
macro_rules! bind_var {
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
index fcee4ee2a..f0443556f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
@@ -1,190 +1,232 @@
+error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`)
+ --> $DIR/unnecessary_cast.rs:19:5
+ |
+LL | ptr as *const T
+ | ^^^^^^^^^^^^^^^ help: try: `ptr`
+ |
+ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
+
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:14:5
+ --> $DIR/unnecessary_cast.rs:44:5
|
LL | 1i32 as i32;
| ^^^^^^^^^^^ help: try: `1_i32`
- |
- = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:15:5
+ --> $DIR/unnecessary_cast.rs:45:5
|
LL | 1f32 as f32;
| ^^^^^^^^^^^ help: try: `1_f32`
error: casting to the same type is unnecessary (`bool` -> `bool`)
- --> $DIR/unnecessary_cast.rs:16:5
+ --> $DIR/unnecessary_cast.rs:46:5
|
LL | false as bool;
| ^^^^^^^^^^^^^ help: try: `false`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:19:5
+ --> $DIR/unnecessary_cast.rs:49:5
|
LL | -1_i32 as i32;
| ^^^^^^^^^^^^^ help: try: `-1_i32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:20:5
+ --> $DIR/unnecessary_cast.rs:50:5
|
LL | - 1_i32 as i32;
| ^^^^^^^^^^^^^^ help: try: `- 1_i32`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:21:5
+ --> $DIR/unnecessary_cast.rs:51:5
|
LL | -1f32 as f32;
| ^^^^^^^^^^^^ help: try: `-1_f32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:22:5
+ --> $DIR/unnecessary_cast.rs:52:5
|
LL | 1_i32 as i32;
| ^^^^^^^^^^^^ help: try: `1_i32`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:23:5
+ --> $DIR/unnecessary_cast.rs:53:5
|
LL | 1_f32 as f32;
| ^^^^^^^^^^^^ help: try: `1_f32`
+error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`)
+ --> $DIR/unnecessary_cast.rs:55:22
+ |
+LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()`
+
+error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`)
+ --> $DIR/unnecessary_cast.rs:57:5
+ |
+LL | [1u8, 2].as_ptr() as *const u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()`
+
+error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`)
+ --> $DIR/unnecessary_cast.rs:59:5
+ |
+LL | [1u8, 2].as_mut_ptr() as *mut u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()`
+
+error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`)
+ --> $DIR/unnecessary_cast.rs:70:5
+ |
+LL | owo::<u32>([1u32].as_ptr()) as *const u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::<u32>([1u32].as_ptr())`
+
+error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`)
+ --> $DIR/unnecessary_cast.rs:71:5
+ |
+LL | uwu::<u32, u8>([1u32].as_ptr()) as *const u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::<u32, u8>([1u32].as_ptr())`
+
+error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`)
+ --> $DIR/unnecessary_cast.rs:73:5
+ |
+LL | uwu::<u32, u32>([1u32].as_ptr()) as *const u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::<u32, u32>([1u32].as_ptr())`
+
error: casting integer literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:64:9
+ --> $DIR/unnecessary_cast.rs:139:9
|
LL | 100 as f32;
| ^^^^^^^^^^ help: try: `100_f32`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:65:9
+ --> $DIR/unnecessary_cast.rs:140:9
|
LL | 100 as f64;
| ^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:66:9
+ --> $DIR/unnecessary_cast.rs:141:9
|
LL | 100_i32 as f64;
| ^^^^^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:67:17
+ --> $DIR/unnecessary_cast.rs:142:17
|
LL | let _ = -100 as f32;
| ^^^^^^^^^^^ help: try: `-100_f32`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:68:17
+ --> $DIR/unnecessary_cast.rs:143:17
|
LL | let _ = -100 as f64;
| ^^^^^^^^^^^ help: try: `-100_f64`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:69:17
+ --> $DIR/unnecessary_cast.rs:144:17
|
LL | let _ = -100_i32 as f64;
| ^^^^^^^^^^^^^^^ help: try: `-100_f64`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:70:9
+ --> $DIR/unnecessary_cast.rs:145:9
|
LL | 100. as f32;
| ^^^^^^^^^^^ help: try: `100_f32`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:71:9
+ --> $DIR/unnecessary_cast.rs:146:9
|
LL | 100. as f64;
| ^^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `u32` is unnecessary
- --> $DIR/unnecessary_cast.rs:83:9
+ --> $DIR/unnecessary_cast.rs:158:9
|
LL | 1 as u32;
| ^^^^^^^^ help: try: `1_u32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:84:9
+ --> $DIR/unnecessary_cast.rs:159:9
|
LL | 0x10 as i32;
| ^^^^^^^^^^^ help: try: `0x10_i32`
error: casting integer literal to `usize` is unnecessary
- --> $DIR/unnecessary_cast.rs:85:9
+ --> $DIR/unnecessary_cast.rs:160:9
|
LL | 0b10 as usize;
| ^^^^^^^^^^^^^ help: try: `0b10_usize`
error: casting integer literal to `u16` is unnecessary
- --> $DIR/unnecessary_cast.rs:86:9
+ --> $DIR/unnecessary_cast.rs:161:9
|
LL | 0o73 as u16;
| ^^^^^^^^^^^ help: try: `0o73_u16`
error: casting integer literal to `u32` is unnecessary
- --> $DIR/unnecessary_cast.rs:87:9
+ --> $DIR/unnecessary_cast.rs:162:9
|
LL | 1_000_000_000 as u32;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:89:9
+ --> $DIR/unnecessary_cast.rs:164:9
|
LL | 1.0 as f64;
| ^^^^^^^^^^ help: try: `1.0_f64`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:90:9
+ --> $DIR/unnecessary_cast.rs:165:9
|
LL | 0.5 as f32;
| ^^^^^^^^^^ help: try: `0.5_f32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:94:17
+ --> $DIR/unnecessary_cast.rs:169:17
|
LL | let _ = -1 as i32;
| ^^^^^^^^^ help: try: `-1_i32`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:95:17
+ --> $DIR/unnecessary_cast.rs:170:17
|
LL | let _ = -1.0 as f32;
| ^^^^^^^^^^^ help: try: `-1.0_f32`
error: casting to the same type is unnecessary (`i32` -> `i32`)
- --> $DIR/unnecessary_cast.rs:101:18
+ --> $DIR/unnecessary_cast.rs:176:18
|
LL | let _ = &(x as i32);
| ^^^^^^^^^^ help: try: `{ x }`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:107:22
+ --> $DIR/unnecessary_cast.rs:182:22
|
LL | let _: i32 = -(1) as i32;
| ^^^^^^^^^^^ help: try: `-1_i32`
error: casting integer literal to `i64` is unnecessary
- --> $DIR/unnecessary_cast.rs:109:22
+ --> $DIR/unnecessary_cast.rs:184:22
|
LL | let _: i64 = -(1) as i64;
| ^^^^^^^^^^^ help: try: `-1_i64`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:116:22
+ --> $DIR/unnecessary_cast.rs:191:22
|
LL | let _: f64 = (-8.0 as f64).exp();
| ^^^^^^^^^^^^^ help: try: `(-8.0_f64)`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:118:23
+ --> $DIR/unnecessary_cast.rs:193:23
|
LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
| ^^^^^^^^^^^^ help: try: `8.0_f64`
error: casting to the same type is unnecessary (`f32` -> `f32`)
- --> $DIR/unnecessary_cast.rs:126:20
+ --> $DIR/unnecessary_cast.rs:201:20
|
LL | let _num = foo() as f32;
| ^^^^^^^^^^^^ help: try: `foo()`
-error: aborting due to 31 previous errors
+error: aborting due to 38 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
index 2bed14973..bd1d4a152 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
@@ -49,4 +49,28 @@ fn unnecessary_fold_over_multiple_lines() {
.any(|x| x > 2);
}
+fn issue10000() {
+ use std::collections::HashMap;
+ use std::hash::BuildHasher;
+
+ fn anything<T>(_: T) {}
+ fn num(_: i32) {}
+ fn smoketest_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
+ map.insert(0, 0);
+ assert_eq!(map.values().sum::<i32>(), 0);
+
+ // more cases:
+ let _ = map.values().sum::<i32>();
+ let _ = map.values().product::<i32>();
+ let _: i32 = map.values().sum();
+ let _: i32 = map.values().product();
+ anything(map.values().sum::<i32>());
+ anything(map.values().product::<i32>());
+ num(map.values().sum());
+ num(map.values().product());
+ }
+
+ smoketest_map(HashMap::new());
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs
index a3cec8ea3..d27cc460c 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs
@@ -49,4 +49,28 @@ fn unnecessary_fold_over_multiple_lines() {
.fold(false, |acc, x| acc || x > 2);
}
+fn issue10000() {
+ use std::collections::HashMap;
+ use std::hash::BuildHasher;
+
+ fn anything<T>(_: T) {}
+ fn num(_: i32) {}
+ fn smoketest_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
+ map.insert(0, 0);
+ assert_eq!(map.values().fold(0, |x, y| x + y), 0);
+
+ // more cases:
+ let _ = map.values().fold(0, |x, y| x + y);
+ let _ = map.values().fold(1, |x, y| x * y);
+ let _: i32 = map.values().fold(0, |x, y| x + y);
+ let _: i32 = map.values().fold(1, |x, y| x * y);
+ anything(map.values().fold(0, |x, y| x + y));
+ anything(map.values().fold(1, |x, y| x * y));
+ num(map.values().fold(0, |x, y| x + y));
+ num(map.values().fold(1, |x, y| x * y));
+ }
+
+ smoketest_map(HashMap::new());
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
index 22c44588a..98979f747 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
@@ -36,5 +36,59 @@ error: this `.fold` can be written more succinctly using another method
LL | .fold(false, |acc, x| acc || x > 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
-error: aborting due to 6 previous errors
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:60:33
+ |
+LL | assert_eq!(map.values().fold(0, |x, y| x + y), 0);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:63:30
+ |
+LL | let _ = map.values().fold(0, |x, y| x + y);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:64:30
+ |
+LL | let _ = map.values().fold(1, |x, y| x * y);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::<i32>()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:65:35
+ |
+LL | let _: i32 = map.values().fold(0, |x, y| x + y);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:66:35
+ |
+LL | let _: i32 = map.values().fold(1, |x, y| x * y);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:67:31
+ |
+LL | anything(map.values().fold(0, |x, y| x + y));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:68:31
+ |
+LL | anything(map.values().fold(1, |x, y| x * y));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::<i32>()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:69:26
+ |
+LL | num(map.values().fold(0, |x, y| x + y));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:70:26
+ |
+LL | num(map.values().fold(1, |x, y| x * y));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
+
+error: aborting due to 15 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.fixed b/src/tools/clippy/tests/ui/unnecessary_join.fixed
index e102df625..f13a5275e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_join.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_join.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::unnecessary_join)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
fn main() {
// should be linted
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.rs b/src/tools/clippy/tests/ui/unnecessary_join.rs
index b87c15bc1..6014d723a 100644
--- a/src/tools/clippy/tests/ui/unnecessary_join.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_join.rs
@@ -1,6 +1,6 @@
//@run-rustfix
#![warn(clippy::unnecessary_join)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
fn main() {
// should be linted
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
index c3728886e..dca380341 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -1,9 +1,13 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::unnecessary_lazy_evaluations)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::map_identity)]
+#![allow(clippy::needless_borrow)]
+#![allow(clippy::unnecessary_literal_unwrap)]
+
+use std::ops::Deref;
extern crate proc_macros;
use proc_macros::with_span;
@@ -41,6 +45,15 @@ impl Drop for Issue9427FollowUp {
}
}
+struct Issue10437;
+impl Deref for Issue10437 {
+ type Target = u32;
+ fn deref(&self) -> &Self::Target {
+ println!("side effect deref");
+ &0
+ }
+}
+
fn main() {
let astronomers_pi = 10;
let ext_arr: [usize; 1] = [2];
@@ -65,6 +78,15 @@ fn main() {
let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
let _ = cond.then_some(astronomers_pi);
+ // Should lint - Builtin deref
+ let r = &1;
+ let _ = Some(1).unwrap_or(*r);
+ let b = Box::new(1);
+ let _ = Some(1).unwrap_or(*b);
+ // Should lint - Builtin deref through autoderef
+ let _ = Some(1).as_ref().unwrap_or(&r);
+ let _ = Some(1).as_ref().unwrap_or(&b);
+
// Cases when unwrap is not called on a simple variable
let _ = Some(10).unwrap_or(2);
let _ = Some(10).and(ext_opt);
@@ -93,6 +115,12 @@ fn main() {
let _ = deep.0.or_else(|| some_call());
let _ = opt.ok_or_else(|| ext_arr[0]);
+ let _ = Some(1).unwrap_or_else(|| *Issue10437); // Issue10437 has a deref impl
+ let _ = Some(1).unwrap_or(*Issue10437);
+
+ let _ = Some(1).as_ref().unwrap_or_else(|| &Issue10437);
+ let _ = Some(1).as_ref().unwrap_or(&Issue10437);
+
// Should not lint - bool
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
index 76e50fa5b..7fda719ed 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -1,9 +1,13 @@
//@run-rustfix
-//@aux-build: proc_macros.rs
+//@aux-build: proc_macros.rs:proc-macro
#![warn(clippy::unnecessary_lazy_evaluations)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::map_identity)]
+#![allow(clippy::needless_borrow)]
+#![allow(clippy::unnecessary_literal_unwrap)]
+
+use std::ops::Deref;
extern crate proc_macros;
use proc_macros::with_span;
@@ -41,6 +45,15 @@ impl Drop for Issue9427FollowUp {
}
}
+struct Issue10437;
+impl Deref for Issue10437 {
+ type Target = u32;
+ fn deref(&self) -> &Self::Target {
+ println!("side effect deref");
+ &0
+ }
+}
+
fn main() {
let astronomers_pi = 10;
let ext_arr: [usize; 1] = [2];
@@ -65,6 +78,15 @@ fn main() {
let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
let _ = cond.then(|| astronomers_pi);
+ // Should lint - Builtin deref
+ let r = &1;
+ let _ = Some(1).unwrap_or_else(|| *r);
+ let b = Box::new(1);
+ let _ = Some(1).unwrap_or_else(|| *b);
+ // Should lint - Builtin deref through autoderef
+ let _ = Some(1).as_ref().unwrap_or_else(|| &r);
+ let _ = Some(1).as_ref().unwrap_or_else(|| &b);
+
// Cases when unwrap is not called on a simple variable
let _ = Some(10).unwrap_or_else(|| 2);
let _ = Some(10).and_then(|_| ext_opt);
@@ -93,6 +115,12 @@ fn main() {
let _ = deep.0.or_else(|| some_call());
let _ = opt.ok_or_else(|| ext_arr[0]);
+ let _ = Some(1).unwrap_or_else(|| *Issue10437); // Issue10437 has a deref impl
+ let _ = Some(1).unwrap_or(*Issue10437);
+
+ let _ = Some(1).as_ref().unwrap_or_else(|| &Issue10437);
+ let _ = Some(1).as_ref().unwrap_or(&Issue10437);
+
// Should not lint - bool
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index 033975544..458eed1f3 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:56:13
+ --> $DIR/unnecessary_lazy_eval.rs:69:13
|
LL | let _ = opt.unwrap_or_else(|| 2);
| ^^^^--------------------
@@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2);
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:57:13
+ --> $DIR/unnecessary_lazy_eval.rs:70:13
|
LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
| ^^^^---------------------------------
@@ -17,7 +17,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:58:13
+ --> $DIR/unnecessary_lazy_eval.rs:71:13
|
LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
| ^^^^-------------------------------------
@@ -25,7 +25,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:60:13
+ --> $DIR/unnecessary_lazy_eval.rs:73:13
|
LL | let _ = opt.and_then(|_| ext_opt);
| ^^^^---------------------
@@ -33,7 +33,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:61:13
+ --> $DIR/unnecessary_lazy_eval.rs:74:13
|
LL | let _ = opt.or_else(|| ext_opt);
| ^^^^-------------------
@@ -41,7 +41,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:62:13
+ --> $DIR/unnecessary_lazy_eval.rs:75:13
|
LL | let _ = opt.or_else(|| None);
| ^^^^----------------
@@ -49,7 +49,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:63:13
+ --> $DIR/unnecessary_lazy_eval.rs:76:13
|
LL | let _ = opt.get_or_insert_with(|| 2);
| ^^^^------------------------
@@ -57,7 +57,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:64:13
+ --> $DIR/unnecessary_lazy_eval.rs:77:13
|
LL | let _ = opt.ok_or_else(|| 2);
| ^^^^----------------
@@ -65,7 +65,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:65:13
+ --> $DIR/unnecessary_lazy_eval.rs:78:13
|
LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
| ^^^^^^^^^^^^^^^^^-------------------------------
@@ -73,7 +73,7 @@ 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:66:13
+ --> $DIR/unnecessary_lazy_eval.rs:79:13
|
LL | let _ = cond.then(|| astronomers_pi);
| ^^^^^-----------------------
@@ -81,7 +81,39 @@ LL | let _ = cond.then(|| astronomers_pi);
| help: use `then_some(..)` instead: `then_some(astronomers_pi)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:69:13
+ --> $DIR/unnecessary_lazy_eval.rs:83:13
+ |
+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:85:13
+ |
+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:87:13
+ |
+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:88:13
+ |
+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:91:13
|
LL | let _ = Some(10).unwrap_or_else(|| 2);
| ^^^^^^^^^--------------------
@@ -89,7 +121,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:70:13
+ --> $DIR/unnecessary_lazy_eval.rs:92:13
|
LL | let _ = Some(10).and_then(|_| ext_opt);
| ^^^^^^^^^---------------------
@@ -97,7 +129,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:71:28
+ --> $DIR/unnecessary_lazy_eval.rs:93:28
|
LL | let _: Option<usize> = None.or_else(|| ext_opt);
| ^^^^^-------------------
@@ -105,7 +137,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:72:13
+ --> $DIR/unnecessary_lazy_eval.rs:94:13
|
LL | let _ = None.get_or_insert_with(|| 2);
| ^^^^^------------------------
@@ -113,7 +145,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:73:35
+ --> $DIR/unnecessary_lazy_eval.rs:95:35
|
LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
| ^^^^^----------------
@@ -121,7 +153,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:74:28
+ --> $DIR/unnecessary_lazy_eval.rs:96:28
|
LL | let _: Option<usize> = None.or_else(|| None);
| ^^^^^----------------
@@ -129,7 +161,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:77:13
+ --> $DIR/unnecessary_lazy_eval.rs:99:13
|
LL | let _ = deep.0.unwrap_or_else(|| 2);
| ^^^^^^^--------------------
@@ -137,7 +169,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:78:13
+ --> $DIR/unnecessary_lazy_eval.rs:100:13
|
LL | let _ = deep.0.and_then(|_| ext_opt);
| ^^^^^^^---------------------
@@ -145,7 +177,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:79:13
+ --> $DIR/unnecessary_lazy_eval.rs:101:13
|
LL | let _ = deep.0.or_else(|| None);
| ^^^^^^^----------------
@@ -153,7 +185,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:80:13
+ --> $DIR/unnecessary_lazy_eval.rs:102:13
|
LL | let _ = deep.0.get_or_insert_with(|| 2);
| ^^^^^^^------------------------
@@ -161,7 +193,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:81:13
+ --> $DIR/unnecessary_lazy_eval.rs:103:13
|
LL | let _ = deep.0.ok_or_else(|| 2);
| ^^^^^^^----------------
@@ -169,7 +201,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:105:28
+ --> $DIR/unnecessary_lazy_eval.rs:133:28
|
LL | let _: Option<usize> = None.or_else(|| Some(3));
| ^^^^^-------------------
@@ -177,7 +209,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:106:13
+ --> $DIR/unnecessary_lazy_eval.rs:134:13
|
LL | let _ = deep.0.or_else(|| Some(3));
| ^^^^^^^-------------------
@@ -185,7 +217,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:107:13
+ --> $DIR/unnecessary_lazy_eval.rs:135:13
|
LL | let _ = opt.or_else(|| Some(3));
| ^^^^-------------------
@@ -193,7 +225,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:113:13
+ --> $DIR/unnecessary_lazy_eval.rs:141:13
|
LL | let _ = res2.unwrap_or_else(|_| 2);
| ^^^^^---------------------
@@ -201,7 +233,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:114:13
+ --> $DIR/unnecessary_lazy_eval.rs:142:13
|
LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
| ^^^^^----------------------------------
@@ -209,7 +241,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:115:13
+ --> $DIR/unnecessary_lazy_eval.rs:143:13
|
LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
| ^^^^^--------------------------------------
@@ -217,7 +249,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:137:35
+ --> $DIR/unnecessary_lazy_eval.rs:165:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
| ^^^^--------------------
@@ -225,7 +257,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:138:35
+ --> $DIR/unnecessary_lazy_eval.rs:166:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
| ^^^^---------------------------------
@@ -233,7 +265,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:139:35
+ --> $DIR/unnecessary_lazy_eval.rs:167:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
| ^^^^-------------------------------------
@@ -241,7 +273,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:141:35
+ --> $DIR/unnecessary_lazy_eval.rs:169:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
| ^^^^------------------
@@ -249,7 +281,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:142:35
+ --> $DIR/unnecessary_lazy_eval.rs:170:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
| ^^^^-------------------------------
@@ -257,7 +289,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:143:35
+ --> $DIR/unnecessary_lazy_eval.rs:171:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
| ^^^^-----------------------------------
@@ -265,7 +297,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:144:35
+ --> $DIR/unnecessary_lazy_eval.rs:172:35
|
LL | let _: Result<usize, usize> = res.
| ___________________________________^
@@ -279,5 +311,5 @@ LL | | or_else(|_| Ok(ext_str.some_field));
| |
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
-error: aborting due to 34 previous errors
+error: aborting due to 38 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 b05dd143b..b4a1f8167 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
@@ -1,4 +1,5 @@
#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::unnecessary_literal_unwrap)]
struct Deep(Option<usize>);
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 20acab6e8..7f353ba06 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -1,5 +1,5 @@
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:13:13
|
LL | let _ = Ok(1).unwrap_or_else(|()| 2);
| ^^^^^^----------------------
@@ -9,7 +9,7 @@ LL | let _ = Ok(1).unwrap_or_else(|()| 2);
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
|
LL | let _ = Ok(1).unwrap_or_else(|e::E| 2);
| ^^^^^^------------------------
@@ -17,7 +17,7 @@ LL | let _ = Ok(1).unwrap_or_else(|e::E| 2);
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:18:13
|
LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
| ^^^^^^-------------------------------------
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed
new file mode 100644
index 000000000..44530d8b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed
@@ -0,0 +1,89 @@
+//@run-rustfix
+#![warn(clippy::unnecessary_literal_unwrap)]
+#![allow(unreachable_code)]
+#![allow(
+ clippy::unnecessary_lazy_evaluations,
+ clippy::diverging_sub_expression,
+ clippy::let_unit_value,
+ clippy::no_effect
+)]
+
+fn unwrap_option_some() {
+ let _val = 1;
+ let _val = 1;
+
+ 1;
+ 1;
+}
+
+#[rustfmt::skip] // force rustfmt not to remove braces in `|| { 234 }`
+fn unwrap_option_none() {
+ let _val = panic!();
+ let _val = panic!("this always happens");
+ let _val: String = String::default();
+ let _val: u16 = 234;
+ let _val: u16 = 234;
+ let _val: u16 = { 234 };
+ let _val: u16 = { 234 };
+
+ panic!();
+ panic!("this always happens");
+ String::default();
+ 234;
+ 234;
+ { 234 };
+ { 234 };
+}
+
+fn unwrap_result_ok() {
+ let _val = 1;
+ let _val = 1;
+ let _val = panic!("{:?}", 1);
+ let _val = panic!("{1}: {:?}", 1, "this always happens");
+
+ 1;
+ 1;
+ panic!("{:?}", 1);
+ panic!("{1}: {:?}", 1, "this always happens");
+}
+
+fn unwrap_result_err() {
+ let _val = 1;
+ let _val = 1;
+ let _val = panic!("{:?}", 1);
+ let _val = panic!("{1}: {:?}", 1, "this always happens");
+
+ 1;
+ 1;
+ panic!("{:?}", 1);
+ panic!("{1}: {:?}", 1, "this always happens");
+}
+
+fn unwrap_methods_option() {
+ let _val = 1;
+ let _val = 1;
+ let _val = 1;
+
+ 1;
+ 1;
+ 1;
+}
+
+fn unwrap_methods_result() {
+ let _val = 1;
+ let _val = 1;
+ let _val = 1;
+
+ 1;
+ 1;
+ 1;
+}
+
+fn main() {
+ unwrap_option_some();
+ unwrap_option_none();
+ unwrap_result_ok();
+ unwrap_result_err();
+ unwrap_methods_option();
+ unwrap_methods_result();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.rs b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.rs
new file mode 100644
index 000000000..b43e4d3a3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.rs
@@ -0,0 +1,89 @@
+//@run-rustfix
+#![warn(clippy::unnecessary_literal_unwrap)]
+#![allow(unreachable_code)]
+#![allow(
+ clippy::unnecessary_lazy_evaluations,
+ clippy::diverging_sub_expression,
+ clippy::let_unit_value,
+ clippy::no_effect
+)]
+
+fn unwrap_option_some() {
+ let _val = Some(1).unwrap();
+ let _val = Some(1).expect("this never happens");
+
+ Some(1).unwrap();
+ Some(1).expect("this never happens");
+}
+
+#[rustfmt::skip] // force rustfmt not to remove braces in `|| { 234 }`
+fn unwrap_option_none() {
+ let _val = None::<()>.unwrap();
+ let _val = None::<()>.expect("this always happens");
+ let _val: String = None.unwrap_or_default();
+ let _val: u16 = None.unwrap_or(234);
+ let _val: u16 = None.unwrap_or_else(|| 234);
+ let _val: u16 = None.unwrap_or_else(|| { 234 });
+ let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 });
+
+ None::<()>.unwrap();
+ None::<()>.expect("this always happens");
+ None::<String>.unwrap_or_default();
+ None::<u16>.unwrap_or(234);
+ None::<u16>.unwrap_or_else(|| 234);
+ None::<u16>.unwrap_or_else(|| { 234 });
+ None::<u16>.unwrap_or_else(|| -> u16 { 234 });
+}
+
+fn unwrap_result_ok() {
+ let _val = Ok::<_, ()>(1).unwrap();
+ let _val = Ok::<_, ()>(1).expect("this never happens");
+ let _val = Ok::<_, ()>(1).unwrap_err();
+ let _val = Ok::<_, ()>(1).expect_err("this always happens");
+
+ Ok::<_, ()>(1).unwrap();
+ Ok::<_, ()>(1).expect("this never happens");
+ Ok::<_, ()>(1).unwrap_err();
+ Ok::<_, ()>(1).expect_err("this always happens");
+}
+
+fn unwrap_result_err() {
+ let _val = Err::<(), _>(1).unwrap_err();
+ let _val = Err::<(), _>(1).expect_err("this never happens");
+ let _val = Err::<(), _>(1).unwrap();
+ let _val = Err::<(), _>(1).expect("this always happens");
+
+ Err::<(), _>(1).unwrap_err();
+ Err::<(), _>(1).expect_err("this never happens");
+ Err::<(), _>(1).unwrap();
+ Err::<(), _>(1).expect("this always happens");
+}
+
+fn unwrap_methods_option() {
+ let _val = Some(1).unwrap_or(2);
+ let _val = Some(1).unwrap_or_default();
+ let _val = Some(1).unwrap_or_else(|| 2);
+
+ Some(1).unwrap_or(2);
+ Some(1).unwrap_or_default();
+ Some(1).unwrap_or_else(|| 2);
+}
+
+fn unwrap_methods_result() {
+ let _val = Ok::<_, ()>(1).unwrap_or(2);
+ let _val = Ok::<_, ()>(1).unwrap_or_default();
+ let _val = Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+
+ Ok::<_, ()>(1).unwrap_or(2);
+ Ok::<_, ()>(1).unwrap_or_default();
+ Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+}
+
+fn main() {
+ unwrap_option_some();
+ unwrap_option_none();
+ unwrap_result_ok();
+ unwrap_result_err();
+ unwrap_methods_option();
+ unwrap_methods_result();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
new file mode 100644
index 000000000..905384bc8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
@@ -0,0 +1,521 @@
+error: used `unwrap()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:12:16
+ |
+LL | let _val = Some(1).unwrap();
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-literal-unwrap` implied by `-D warnings`
+help: remove the `Some` and `unwrap()`
+ |
+LL - let _val = Some(1).unwrap();
+LL + let _val = 1;
+ |
+
+error: used `expect()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:13:16
+ |
+LL | let _val = Some(1).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `expect()`
+ |
+LL - let _val = Some(1).expect("this never happens");
+LL + let _val = 1;
+ |
+
+error: used `unwrap()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:15:5
+ |
+LL | Some(1).unwrap();
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap()`
+ |
+LL - Some(1).unwrap();
+LL + 1;
+ |
+
+error: used `expect()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:16:5
+ |
+LL | Some(1).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `expect()`
+ |
+LL - Some(1).expect("this never happens");
+LL + 1;
+ |
+
+error: used `unwrap()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:21:16
+ |
+LL | let _val = None::<()>.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap()`: `panic!()`
+
+error: used `expect()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:22:16
+ |
+LL | let _val = None::<()>.expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `expect()`
+ |
+LL | let _val = panic!("this always happens");
+ | ~~~~~~~ ~
+
+error: used `unwrap_or_default()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:23:24
+ |
+LL | let _val: String = None.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()`
+
+error: used `unwrap_or()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:24:21
+ |
+LL | let _val: u16 = None.unwrap_or(234);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or()`
+ |
+LL - let _val: u16 = None.unwrap_or(234);
+LL + let _val: u16 = 234;
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:25:21
+ |
+LL | let _val: u16 = None.unwrap_or_else(|| 234);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or_else()`
+ |
+LL - let _val: u16 = None.unwrap_or_else(|| 234);
+LL + let _val: u16 = 234;
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:26:21
+ |
+LL | let _val: u16 = None.unwrap_or_else(|| { 234 });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or_else()`
+ |
+LL - let _val: u16 = None.unwrap_or_else(|| { 234 });
+LL + let _val: u16 = { 234 };
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:27:21
+ |
+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 };
+ |
+
+error: used `unwrap()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:29:5
+ |
+LL | None::<()>.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap()`: `panic!()`
+
+error: used `expect()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:30:5
+ |
+LL | None::<()>.expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `expect()`
+ |
+LL | panic!("this always happens");
+ | ~~~~~~~ ~
+
+error: used `unwrap_or_default()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:31:5
+ |
+LL | None::<String>.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()`
+
+error: used `unwrap_or()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:32:5
+ |
+LL | None::<u16>.unwrap_or(234);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or()`
+ |
+LL - None::<u16>.unwrap_or(234);
+LL + 234;
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:33:5
+ |
+LL | None::<u16>.unwrap_or_else(|| 234);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or_else()`
+ |
+LL - None::<u16>.unwrap_or_else(|| 234);
+LL + 234;
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:34:5
+ |
+LL | None::<u16>.unwrap_or_else(|| { 234 });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap_or_else()`
+ |
+LL - None::<u16>.unwrap_or_else(|| { 234 });
+LL + { 234 };
+ |
+
+error: used `unwrap_or_else()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap.rs:35:5
+ |
+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 };
+ |
+
+error: used `unwrap()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:39:16
+ |
+LL | let _val = Ok::<_, ()>(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap()`
+ |
+LL - let _val = Ok::<_, ()>(1).unwrap();
+LL + let _val = 1;
+ |
+
+error: used `expect()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:40:16
+ |
+LL | let _val = Ok::<_, ()>(1).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect()`
+ |
+LL - let _val = Ok::<_, ()>(1).expect("this never happens");
+LL + let _val = 1;
+ |
+
+error: used `unwrap_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:41:16
+ |
+LL | let _val = Ok::<_, ()>(1).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_err()`
+ |
+LL | let _val = panic!("{:?}", 1);
+ | ~~~~~~~~~~~~~~ ~
+
+error: used `expect_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:42:16
+ |
+LL | let _val = Ok::<_, ()>(1).expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect_err()`
+ |
+LL | let _val = panic!("{1}: {:?}", 1, "this always happens");
+ | ~~~~~~~~~~~~~~~~~~~ ~
+
+error: used `unwrap()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:44:5
+ |
+LL | Ok::<_, ()>(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap()`
+ |
+LL - Ok::<_, ()>(1).unwrap();
+LL + 1;
+ |
+
+error: used `expect()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:45:5
+ |
+LL | Ok::<_, ()>(1).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect()`
+ |
+LL - Ok::<_, ()>(1).expect("this never happens");
+LL + 1;
+ |
+
+error: used `unwrap_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:46:5
+ |
+LL | Ok::<_, ()>(1).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_err()`
+ |
+LL | panic!("{:?}", 1);
+ | ~~~~~~~~~~~~~~ ~
+
+error: used `expect_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:47:5
+ |
+LL | Ok::<_, ()>(1).expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect_err()`
+ |
+LL | panic!("{1}: {:?}", 1, "this always happens");
+ | ~~~~~~~~~~~~~~~~~~~ ~
+
+error: used `unwrap_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:51:16
+ |
+LL | let _val = Err::<(), _>(1).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap_err()`
+ |
+LL - let _val = Err::<(), _>(1).unwrap_err();
+LL + let _val = 1;
+ |
+
+error: used `expect_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:52:16
+ |
+LL | let _val = Err::<(), _>(1).expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect_err()`
+ |
+LL - let _val = Err::<(), _>(1).expect_err("this never happens");
+LL + let _val = 1;
+ |
+
+error: used `unwrap()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:53:16
+ |
+LL | let _val = Err::<(), _>(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap()`
+ |
+LL | let _val = panic!("{:?}", 1);
+ | ~~~~~~~~~~~~~~ ~
+
+error: used `expect()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:54:16
+ |
+LL | let _val = Err::<(), _>(1).expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect()`
+ |
+LL | let _val = panic!("{1}: {:?}", 1, "this always happens");
+ | ~~~~~~~~~~~~~~~~~~~ ~
+
+error: used `unwrap_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:56:5
+ |
+LL | Err::<(), _>(1).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap_err()`
+ |
+LL - Err::<(), _>(1).unwrap_err();
+LL + 1;
+ |
+
+error: used `expect_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:57:5
+ |
+LL | Err::<(), _>(1).expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect_err()`
+ |
+LL - Err::<(), _>(1).expect_err("this never happens");
+LL + 1;
+ |
+
+error: used `unwrap()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:58:5
+ |
+LL | Err::<(), _>(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap()`
+ |
+LL | panic!("{:?}", 1);
+ | ~~~~~~~~~~~~~~ ~
+
+error: used `expect()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap.rs:59:5
+ |
+LL | Err::<(), _>(1).expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect()`
+ |
+LL | panic!("{1}: {:?}", 1, "this always happens");
+ | ~~~~~~~~~~~~~~~~~~~ ~
+
+error: used `unwrap_or()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:63:16
+ |
+LL | let _val = Some(1).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or()`
+ |
+LL - let _val = Some(1).unwrap_or(2);
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or_default()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:64:16
+ |
+LL | let _val = Some(1).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_default()`
+ |
+LL - let _val = Some(1).unwrap_or_default();
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or_else()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:65:16
+ |
+LL | let _val = Some(1).unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_else()`
+ |
+LL - let _val = Some(1).unwrap_or_else(|| 2);
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:67:5
+ |
+LL | Some(1).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or()`
+ |
+LL - Some(1).unwrap_or(2);
+LL + 1;
+ |
+
+error: used `unwrap_or_default()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:68:5
+ |
+LL | Some(1).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_default()`
+ |
+LL - Some(1).unwrap_or_default();
+LL + 1;
+ |
+
+error: used `unwrap_or_else()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap.rs:69:5
+ |
+LL | Some(1).unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_else()`
+ |
+LL - Some(1).unwrap_or_else(|| 2);
+LL + 1;
+ |
+
+error: used `unwrap_or()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:73:16
+ |
+LL | let _val = Ok::<_, ()>(1).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or()`
+ |
+LL - let _val = Ok::<_, ()>(1).unwrap_or(2);
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or_default()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:74:16
+ |
+LL | let _val = Ok::<_, ()>(1).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_default()`
+ |
+LL - let _val = Ok::<_, ()>(1).unwrap_or_default();
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or_else()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:75:16
+ |
+LL | let _val = Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_else()`
+ |
+LL - let _val = Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+LL + let _val = 1;
+ |
+
+error: used `unwrap_or()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:77:5
+ |
+LL | Ok::<_, ()>(1).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or()`
+ |
+LL - Ok::<_, ()>(1).unwrap_or(2);
+LL + 1;
+ |
+
+error: used `unwrap_or_default()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:78:5
+ |
+LL | Ok::<_, ()>(1).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_default()`
+ |
+LL - Ok::<_, ()>(1).unwrap_or_default();
+LL + 1;
+ |
+
+error: used `unwrap_or_else()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap.rs:79:5
+ |
+LL | Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_else()`
+ |
+LL - Ok::<_, ()>(1).unwrap_or_else(|_| 2);
+LL + 1;
+ |
+
+error: aborting due to 46 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.rs
new file mode 100644
index 000000000..41300aceb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.rs
@@ -0,0 +1,118 @@
+#![warn(clippy::unnecessary_literal_unwrap)]
+#![allow(unreachable_code)]
+#![allow(clippy::unnecessary_lazy_evaluations, clippy::let_unit_value)]
+
+fn unwrap_option_some() {
+ let val = Some(1);
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this never happens");
+}
+
+fn unwrap_option_some_context() {
+ let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap();
+ let _val = Some::<usize>([1, 2, 3].iter().sum()).expect("this never happens");
+
+ let val = Some::<usize>([1, 2, 3].iter().sum());
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this never happens");
+}
+
+fn unwrap_option_none() {
+ let val = None::<()>;
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this always happens");
+ let _val3: u8 = None.unwrap_or_default();
+ None::<()>.unwrap_or_default();
+}
+
+fn unwrap_result_ok() {
+ let val = Ok::<_, ()>(1);
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this never happens");
+ let _val2 = val.unwrap_err();
+ let _val2 = val.expect_err("this always happens");
+}
+
+fn unwrap_result_ok_context() {
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap();
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect("this never happens");
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_err();
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect_err("this always happens");
+
+ let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this never happens");
+ let _val2 = val.unwrap_err();
+ let _val2 = val.expect_err("this always happens");
+}
+
+fn unwrap_result_err() {
+ let val = Err::<(), _>(1);
+ let _val2 = val.unwrap_err();
+ let _val2 = val.expect_err("this never happens");
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this always happens");
+}
+
+fn unwrap_result_err_context() {
+ let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap_err();
+ let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect_err("this never happens");
+ let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap();
+ let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect("this always happens");
+
+ let val = Err::<(), usize>([1, 2, 3].iter().sum());
+ let _val2 = val.unwrap_err();
+ let _val2 = val.expect_err("this never happens");
+ let _val2 = val.unwrap();
+ let _val2 = val.expect("this always happens");
+}
+
+fn unwrap_methods_option() {
+ let val = Some(1);
+ let _val2 = val.unwrap_or(2);
+ let _val2 = val.unwrap_or_default();
+ let _val2 = val.unwrap_or_else(|| 2);
+}
+
+fn unwrap_methods_option_context() {
+ let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or(2);
+ let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_default();
+ let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_else(|| 2);
+
+ let val = Some::<usize>([1, 2, 3].iter().sum());
+ let _val2 = val.unwrap_or(2);
+ let _val2 = val.unwrap_or_default();
+ let _val2 = val.unwrap_or_else(|| 2);
+}
+
+fn unwrap_methods_result() {
+ let val = Ok::<_, ()>(1);
+ let _val2 = val.unwrap_or(2);
+ let _val2 = val.unwrap_or_default();
+ let _val2 = val.unwrap_or_else(|_| 2);
+}
+
+fn unwrap_methods_result_context() {
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or(2);
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_default();
+ let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_else(|_| 2);
+
+ let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ let _val2 = val.unwrap_or(2);
+ let _val2 = val.unwrap_or_default();
+ let _val2 = val.unwrap_or_else(|_| 2);
+}
+
+fn main() {
+ unwrap_option_some();
+ unwrap_option_some_context();
+ unwrap_option_none();
+ unwrap_result_ok();
+ unwrap_result_ok_context();
+ unwrap_result_err();
+ unwrap_result_err_context();
+ unwrap_methods_option();
+ unwrap_methods_option_context();
+ unwrap_methods_result();
+ unwrap_methods_result_context();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.stderr
new file mode 100644
index 000000000..2d1270d47
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap_unfixable.stderr
@@ -0,0 +1,615 @@
+error: used `unwrap()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:7:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:6:15
+ |
+LL | let val = Some(1);
+ | ^^^^^^^
+ = note: `-D clippy::unnecessary-literal-unwrap` implied by `-D warnings`
+
+error: used `expect()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:8:17
+ |
+LL | let _val2 = val.expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:6:15
+ |
+LL | let val = Some(1);
+ | ^^^^^^^
+
+error: used `unwrap()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:12:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:12:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:13:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:13:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:16:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:15:15
+ |
+LL | let val = Some::<usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:17:17
+ |
+LL | let _val2 = val.expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:15:15
+ |
+LL | let val = Some::<usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:22:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `None` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:21:15
+ |
+LL | let val = None::<()>;
+ | ^^^^^^^^^^
+
+error: used `expect()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:23:17
+ |
+LL | let _val2 = val.expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `None` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:21:15
+ |
+LL | let val = None::<()>;
+ | ^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:24:21
+ |
+LL | let _val3: u8 = None.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `Default::default()`
+
+error: used `unwrap_or_default()` on `None` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:25:5
+ |
+LL | None::<()>.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `Default::default()`
+
+error: used `unwrap()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:30:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `expect()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:31:17
+ |
+LL | let _val2 = val.expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:32:17
+ |
+LL | let _val2 = val.unwrap_err();
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:33:17
+ |
+LL | let _val2 = val.expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:39:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:39:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:43:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:44:17
+ |
+LL | let _val2 = val.expect("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:45:17
+ |
+LL | let _val2 = val.unwrap_err();
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:46:17
+ |
+LL | let _val2 = val.expect_err("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:51:17
+ |
+LL | let _val2 = val.unwrap_err();
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15
+ |
+LL | let val = Err::<(), _>(1);
+ | ^^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:52:17
+ |
+LL | let _val2 = val.expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15
+ |
+LL | let val = Err::<(), _>(1);
+ | ^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:53:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15
+ |
+LL | let val = Err::<(), _>(1);
+ | ^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:54:17
+ |
+LL | let _val2 = val.expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15
+ |
+LL | let val = Err::<(), _>(1);
+ | ^^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap_err();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:60:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:60:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:16
+ |
+LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:64:17
+ |
+LL | let _val2 = val.unwrap_err();
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15
+ |
+LL | let val = Err::<(), usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect_err()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:65:17
+ |
+LL | let _val2 = val.expect_err("this never happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect_err()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15
+ |
+LL | let val = Err::<(), usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:66:17
+ |
+LL | let _val2 = val.unwrap();
+ | ^^^^^^^^^^^^
+ |
+help: remove the `Err` and `unwrap()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15
+ |
+LL | let val = Err::<(), usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `expect()` on `Err` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:67:17
+ |
+LL | let _val2 = val.expect("this always happens");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Err` and `expect()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15
+ |
+LL | let val = Err::<(), usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:72:17
+ |
+LL | let _val2 = val.unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15
+ |
+LL | let val = Some(1);
+ | ^^^^^^^
+
+error: used `unwrap_or_default()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:73:17
+ |
+LL | let _val2 = val.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15
+ |
+LL | let val = Some(1);
+ | ^^^^^^^
+
+error: used `unwrap_or_else()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:74:17
+ |
+LL | let _val2 = val.unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15
+ |
+LL | let val = Some(1);
+ | ^^^^^^^
+
+error: used `unwrap_or()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:79:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:79:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_else()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:16
+ |
+LL | let _val = Some::<usize>([1, 2, 3].iter().sum()).unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:83:17
+ |
+LL | let _val2 = val.unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15
+ |
+LL | let val = Some::<usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:84:17
+ |
+LL | let _val2 = val.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15
+ |
+LL | let val = Some::<usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_else()` on `Some` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:85:17
+ |
+LL | let _val2 = val.unwrap_or_else(|| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Some` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15
+ |
+LL | let val = Some::<usize>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:90:17
+ |
+LL | let _val2 = val.unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:91:17
+ |
+LL | let _val2 = val.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `unwrap_or_else()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:92:17
+ |
+LL | let _val2 = val.unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15
+ |
+LL | let val = Ok::<_, ()>(1);
+ | ^^^^^^^^^^^^^^
+
+error: used `unwrap_or()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:97:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:97:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_else()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:16
+ |
+LL | let _val = Ok::<usize, ()>([1, 2, 3].iter().sum()).unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:101:17
+ |
+LL | let _val2 = val.unwrap_or(2);
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_default()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:102:17
+ |
+LL | let _val2 = val.unwrap_or_default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_default()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap_or_else()` on `Ok` value
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:103:17
+ |
+LL | let _val2 = val.unwrap_or_else(|_| 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove the `Ok` and `unwrap_or_else()`
+ --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15
+ |
+LL | let val = Ok::<usize, ()>([1, 2, 3].iter().sum());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 52 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
index 89fedb145..d858701ae 100644
--- a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
@@ -1,5 +1,5 @@
#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
-#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
+#![allow(clippy::let_unit_value, clippy::missing_safety_doc, clippy::needless_if)]
mod unsafe_items_invalid_comment {
// SAFETY:
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
index 165cabd82..19380ad00 100644
--- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(clippy::stable_sort_primitive)]
+#![allow(clippy::stable_sort_primitive, clippy::useless_vec)]
use std::cell::Ref;
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
index 8a2158d5a..cea1b65b5 100644
--- a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(clippy::stable_sort_primitive)]
+#![allow(clippy::stable_sort_primitive, clippy::useless_vec)]
use std::cell::Ref;
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
index bdf746cf2..eae1271d1 100644
--- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused)]
+#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)]
#![warn(clippy::unnecessary_struct_initialization)]
struct S {
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
index 7271e2f95..4abd560f8 100644
--- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
@@ -1,6 +1,6 @@
//@run-rustfix
-#![allow(unused)]
+#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)]
#![warn(clippy::unnecessary_struct_initialization)]
struct S {
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index 08733906b..592a53f3a 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -474,3 +474,36 @@ mod issue_10021 {
Ok(())
}
}
+
+mod issue_10033 {
+ #![allow(dead_code)]
+ use std::{fmt::Display, ops::Deref};
+
+ fn _main() {
+ let f = Foo;
+
+ // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does
+ // deref to `str`)
+ foo(&f.to_string());
+ }
+
+ fn foo(s: &str) {
+ println!("{}", s);
+ }
+
+ struct Foo;
+
+ impl Deref for Foo {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "str"
+ }
+ }
+
+ impl Display for Foo {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Foo")
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index e3589ea0d..f2e48b1c4 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -474,3 +474,36 @@ mod issue_10021 {
Ok(())
}
}
+
+mod issue_10033 {
+ #![allow(dead_code)]
+ use std::{fmt::Display, ops::Deref};
+
+ fn _main() {
+ let f = Foo;
+
+ // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does
+ // deref to `str`)
+ foo(&f.to_string());
+ }
+
+ fn foo(s: &str) {
+ println!("{}", s);
+ }
+
+ struct Foo;
+
+ impl Deref for Foo {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "str"
+ }
+ }
+
+ impl Display for Foo {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Foo")
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
index 373b18470..2d55dc664 100644
--- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:proc_macros.rs:proc-macro
#![allow(clippy::let_unit_value)]
#![warn(clippy::unnecessary_safety_doc)]
diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs
index fa639aa70..48ae1cf66 100644
--- a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs
+++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs
@@ -1,5 +1,9 @@
+//@aux-build:proc_macros.rs:proc-macro
#![warn(clippy::unneeded_field_pattern)]
-#[allow(dead_code, unused)]
+#![allow(dead_code, unused)]
+
+#[macro_use]
+extern crate proc_macros;
struct Foo {
a: i32,
@@ -19,4 +23,12 @@ fn main() {
Foo { b: 0, .. } => {}, // should be OK
Foo { .. } => {}, // and the Force might be with this one
}
+ external! {
+ let f = Foo { a: 0, b: 0, c: 0 };
+ match f {
+ Foo { a: _, b: 0, .. } => {},
+
+ Foo { a: _, b: _, c: _ } => {},
+ }
+ }
}
diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
index 6f7c31545..3f1568498 100644
--- a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
+++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
@@ -1,5 +1,5 @@
error: you matched a field with a wildcard pattern, consider using `..` instead
- --> $DIR/unneeded_field_pattern.rs:14:15
+ --> $DIR/unneeded_field_pattern.rs:18:15
|
LL | Foo { a: _, b: 0, .. } => {},
| ^^^^
@@ -8,7 +8,7 @@ LL | Foo { a: _, b: 0, .. } => {},
= note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
error: all the struct fields are matched to a wildcard pattern, consider using `..`
- --> $DIR/unneeded_field_pattern.rs:16:9
+ --> $DIR/unneeded_field_pattern.rs:20:9
|
LL | Foo { a: _, b: _, c: _ } => {},
| ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
index 16c2de760..2eeba509e 100644
--- a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
@@ -1,6 +1,11 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![feature(stmt_expr_attributes)]
#![deny(clippy::unneeded_wildcard_pattern)]
+#![allow(clippy::needless_if)]
+
+#[macro_use]
+extern crate proc_macros;
fn main() {
let t = (0, 1, 2, 3);
@@ -42,4 +47,8 @@ fn main() {
{
if let S(0, ..,) = s {};
}
+ external! {
+ let t = (0, 1, 2, 3);
+ if let (0, _, ..) = t {};
+ }
}
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
index 9d9eae1d9..5416cfaa5 100644
--- a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
@@ -1,6 +1,11 @@
//@run-rustfix
+//@aux-build:proc_macros.rs:proc-macro
#![feature(stmt_expr_attributes)]
#![deny(clippy::unneeded_wildcard_pattern)]
+#![allow(clippy::needless_if)]
+
+#[macro_use]
+extern crate proc_macros;
fn main() {
let t = (0, 1, 2, 3);
@@ -42,4 +47,8 @@ fn main() {
{
if let S(0, .., _, _,) = s {};
}
+ external! {
+ let t = (0, 1, 2, 3);
+ if let (0, _, ..) = t {};
+ }
}
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
index 716d9ecff..ffbdc0495 100644
--- a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
@@ -1,89 +1,89 @@
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:8:18
+ --> $DIR/unneeded_wildcard_pattern.rs:13:18
|
LL | if let (0, .., _) = t {};
| ^^^ help: remove it
|
note: the lint level is defined here
- --> $DIR/unneeded_wildcard_pattern.rs:3:9
+ --> $DIR/unneeded_wildcard_pattern.rs:4:9
|
LL | #![deny(clippy::unneeded_wildcard_pattern)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:9:16
+ --> $DIR/unneeded_wildcard_pattern.rs:14:16
|
LL | if let (0, _, ..) = t {};
| ^^^ help: remove it
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:10:13
+ --> $DIR/unneeded_wildcard_pattern.rs:15:13
|
LL | if let (_, .., 0) = t {};
| ^^^ help: remove it
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:11:15
+ --> $DIR/unneeded_wildcard_pattern.rs:16:15
|
LL | if let (.., _, 0) = t {};
| ^^^ help: remove it
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:12:16
+ --> $DIR/unneeded_wildcard_pattern.rs:17:16
|
LL | if let (0, _, _, ..) = t {};
| ^^^^^^ help: remove them
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:13:18
+ --> $DIR/unneeded_wildcard_pattern.rs:18:18
|
LL | if let (0, .., _, _) = t {};
| ^^^^^^ help: remove them
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:22:22
+ --> $DIR/unneeded_wildcard_pattern.rs:27:22
|
LL | if let (0, .., _, _,) = t {};
| ^^^^^^ help: remove them
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:29:19
+ --> $DIR/unneeded_wildcard_pattern.rs:34:19
|
LL | if let S(0, .., _) = s {};
| ^^^ help: remove it
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:30:17
+ --> $DIR/unneeded_wildcard_pattern.rs:35:17
|
LL | if let S(0, _, ..) = s {};
| ^^^ help: remove it
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:31:14
+ --> $DIR/unneeded_wildcard_pattern.rs:36:14
|
LL | if let S(_, .., 0) = s {};
| ^^^ help: remove it
error: this pattern is unneeded as the `..` pattern can match that element
- --> $DIR/unneeded_wildcard_pattern.rs:32:16
+ --> $DIR/unneeded_wildcard_pattern.rs:37:16
|
LL | if let S(.., _, 0) = s {};
| ^^^ help: remove it
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:33:17
+ --> $DIR/unneeded_wildcard_pattern.rs:38:17
|
LL | if let S(0, _, _, ..) = s {};
| ^^^^^^ help: remove them
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:34:19
+ --> $DIR/unneeded_wildcard_pattern.rs:39:19
|
LL | if let S(0, .., _, _) = s {};
| ^^^^^^ help: remove them
error: these patterns are unneeded as the `..` pattern can match those elements
- --> $DIR/unneeded_wildcard_pattern.rs:43:23
+ --> $DIR/unneeded_wildcard_pattern.rs:48:23
|
LL | if let S(0, .., _, _,) = s {};
| ^^^^^^ help: remove them
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
index 8ec35ba4e..738045595 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
@@ -2,7 +2,13 @@
#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
-#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
+#![allow(
+ clippy::cognitive_complexity,
+ clippy::match_ref_pats,
+ clippy::upper_case_acronyms,
+ clippy::needless_if,
+ clippy::manual_range_patterns
+)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
index efdb91b24..9e0e7b5de 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
@@ -2,7 +2,13 @@
#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
-#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
+#![allow(
+ clippy::cognitive_complexity,
+ clippy::match_ref_pats,
+ clippy::upper_case_acronyms,
+ clippy::needless_if,
+ clippy::manual_range_patterns
+)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
index a1f193db5..b997e4ce8 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
@@ -1,5 +1,5 @@
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:12:12
+ --> $DIR/unnested_or_patterns.rs:18:12
|
LL | if let box 0 | box 2 = Box::new(0) {}
| ^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {}
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:13:12
+ --> $DIR/unnested_or_patterns.rs:19:12
|
LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
| ~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:15:12
+ --> $DIR/unnested_or_patterns.rs:21:12
|
LL | if let Some(1) | C0 | Some(2) = None {}
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL | if let Some(1 | 2) | C0 = None {}
| ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:16:12
+ --> $DIR/unnested_or_patterns.rs:22:12
|
LL | if let &mut 0 | &mut 2 = &mut 0 {}
| ^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {}
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:17:12
+ --> $DIR/unnested_or_patterns.rs:23:12
|
LL | if let x @ 0 | x @ 2 = 0 {}
| ^^^^^^^^^^^^^
@@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {}
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:18:12
+ --> $DIR/unnested_or_patterns.rs:24:12
|
LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:19:12
+ --> $DIR/unnested_or_patterns.rs:25:12
|
LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:20:12
+ --> $DIR/unnested_or_patterns.rs:26:12
|
LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {}
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:21:12
+ --> $DIR/unnested_or_patterns.rs:27:12
|
LL | if let [0] | [1] = [0] {}
| ^^^^^^^^^
@@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {}
| ~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:22:12
+ --> $DIR/unnested_or_patterns.rs:28:12
|
LL | if let [x, 0] | [x, 1] = [0, 1] {}
| ^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {}
| ~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:23:12
+ --> $DIR/unnested_or_patterns.rs:29:12
|
LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:24:12
+ --> $DIR/unnested_or_patterns.rs:30:12
|
LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {}
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:26:12
+ --> $DIR/unnested_or_patterns.rs:32:12
|
LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^
@@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {}
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:27:12
+ --> $DIR/unnested_or_patterns.rs:33:12
|
LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
| ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:28:12
+ --> $DIR/unnested_or_patterns.rs:34:12
|
LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
| ~~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:33:12
+ --> $DIR/unnested_or_patterns.rs:39:12
|
LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -176,7 +176,7 @@ LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
| ~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:44:12
+ --> $DIR/unnested_or_patterns.rs:50:12
|
LL | if let [1] | [53] = [0] {}
| ^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed
index de40e9367..11dc34378 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed
@@ -2,7 +2,12 @@
#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
-#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
+#![allow(
+ clippy::cognitive_complexity,
+ clippy::match_ref_pats,
+ clippy::needless_if,
+ clippy::manual_range_patterns
+)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs
index 87f66d26c..b25560827 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs
@@ -2,7 +2,12 @@
#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
-#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
+#![allow(
+ clippy::cognitive_complexity,
+ clippy::match_ref_pats,
+ clippy::needless_if,
+ clippy::manual_range_patterns
+)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
index 41e8d3fc7..76e890b3a 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
@@ -1,5 +1,5 @@
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:9:12
+ --> $DIR/unnested_or_patterns2.rs:14:12
|
LL | if let Some(Some(0)) | Some(Some(1)) = None {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | if let Some(Some(0 | 1)) = None {}
| ~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:10:12
+ --> $DIR/unnested_or_patterns2.rs:15:12
|
LL | if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | if let Some(Some(0 | 1 | 2)) = None {}
| ~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:11:12
+ --> $DIR/unnested_or_patterns2.rs:16:12
|
LL | if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL | if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:12:12
+ --> $DIR/unnested_or_patterns2.rs:17:12
|
LL | if let Some(Some(0) | Some(1 | 2)) = None {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL | if let Some(Some(0 | 1 | 2)) = None {}
| ~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:13:12
+ --> $DIR/unnested_or_patterns2.rs:18:12
|
LL | if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -55,7 +55,7 @@ LL | if let ((0 | 1 | 2,),) = ((0,),) {}
| ~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:14:12
+ --> $DIR/unnested_or_patterns2.rs:19:12
|
LL | if let 0 | (1 | 2) = 0 {}
| ^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL | if let 0 | 1 | 2 = 0 {}
| ~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:15:12
+ --> $DIR/unnested_or_patterns2.rs:20:12
|
LL | if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
| ~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns2.rs:16:12
+ --> $DIR/unnested_or_patterns2.rs:21:12
|
LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
index b6241612d..125120872 100644
--- a/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
+++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![warn(clippy::unseparated_literal_suffix)]
#![allow(dead_code)]
diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
index ae583f4bd..0a3ffc478 100644
--- a/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
+++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![warn(clippy::unseparated_literal_suffix)]
#![allow(dead_code)]
diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs
index 4ca7f29b3..69e46ab47 100644
--- a/src/tools/clippy/tests/ui/unused_async.rs
+++ b/src/tools/clippy/tests/ui/unused_async.rs
@@ -1,8 +1,42 @@
#![warn(clippy::unused_async)]
+#![feature(async_fn_in_trait)]
+#![allow(incomplete_features)]
use std::future::Future;
use std::pin::Pin;
+mod issue10800 {
+ #![allow(dead_code, unused_must_use, clippy::no_effect)]
+
+ use std::future::ready;
+
+ async fn async_block_await() {
+ async {
+ ready(()).await;
+ };
+ }
+
+ async fn normal_block_await() {
+ {
+ {
+ ready(()).await;
+ }
+ }
+ }
+}
+
+mod issue10459 {
+ trait HasAsyncMethod {
+ async fn do_something() -> u32;
+ }
+
+ impl HasAsyncMethod for () {
+ async fn do_something() -> u32 {
+ 1
+ }
+ }
+}
+
async fn foo() -> i32 {
4
}
diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr
index cff3eccbd..ffae8366b 100644
--- a/src/tools/clippy/tests/ui/unused_async.stderr
+++ b/src/tools/clippy/tests/ui/unused_async.stderr
@@ -1,5 +1,23 @@
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:6:1
+ --> $DIR/unused_async.rs:13:5
+ |
+LL | / async fn async_block_await() {
+LL | | async {
+LL | | ready(()).await;
+LL | | };
+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:15:23
+ |
+LL | ready(()).await;
+ | ^^^^^
+ = note: `-D clippy::unused-async` implied by `-D warnings`
+
+error: unused `async` for function with no await statements
+ --> $DIR/unused_async.rs:40:1
|
LL | / async fn foo() -> i32 {
LL | | 4
@@ -7,10 +25,9 @@ LL | | }
| |_^
|
= help: consider removing the `async` from this function
- = note: `-D clippy::unused-async` implied by `-D warnings`
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:17:5
+ --> $DIR/unused_async.rs:51:5
|
LL | / async fn unused(&self) -> i32 {
LL | | 1
@@ -19,5 +36,5 @@ LL | | }
|
= help: consider removing the `async` from this function
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs
index d9fd402e7..64d643783 100644
--- a/src/tools/clippy/tests/ui/unwrap.rs
+++ b/src/tools/clippy/tests/ui/unwrap.rs
@@ -1,4 +1,5 @@
#![warn(clippy::unwrap_used)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn unwrap_option() {
let opt = Some(0);
diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr
index d49bf2b32..3796d942f 100644
--- a/src/tools/clippy/tests/ui/unwrap.stderr
+++ b/src/tools/clippy/tests/ui/unwrap.stderr
@@ -1,5 +1,5 @@
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap.rs:5:13
+ --> $DIR/unwrap.rs:6:13
|
LL | let _ = opt.unwrap();
| ^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let _ = opt.unwrap();
= note: `-D clippy::unwrap-used` implied by `-D warnings`
error: used `unwrap()` on a `Result` value
- --> $DIR/unwrap.rs:10:13
+ --> $DIR/unwrap.rs:11:13
|
LL | let _ = res.unwrap();
| ^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | let _ = res.unwrap();
= help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
error: used `unwrap_err()` on a `Result` value
- --> $DIR/unwrap.rs:11:13
+ --> $DIR/unwrap.rs:12:13
|
LL | let _ = res.unwrap_err();
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs
index 9f27fef82..7f57efc53 100644
--- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs
+++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs
@@ -1,4 +1,5 @@
#![warn(clippy::unwrap_used, clippy::expect_used)]
+#![allow(clippy::unnecessary_literal_unwrap)]
trait OptionExt {
type Item;
diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr
index fe4ecef11..1a551ab5a 100644
--- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr
+++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr
@@ -1,5 +1,5 @@
error: used `unwrap()` on an `Option` value
- --> $DIR/unwrap_expect_used.rs:23:5
+ --> $DIR/unwrap_expect_used.rs:24:5
|
LL | Some(3).unwrap();
| ^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | Some(3).unwrap();
= note: `-D clippy::unwrap-used` implied by `-D warnings`
error: used `expect()` on an `Option` value
- --> $DIR/unwrap_expect_used.rs:24:5
+ --> $DIR/unwrap_expect_used.rs:25:5
|
LL | Some(3).expect("Hello world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL | Some(3).expect("Hello world!");
= note: `-D clippy::expect-used` implied by `-D warnings`
error: used `unwrap()` on a `Result` value
- --> $DIR/unwrap_expect_used.rs:31:5
+ --> $DIR/unwrap_expect_used.rs:32:5
|
LL | a.unwrap();
| ^^^^^^^^^^
@@ -25,7 +25,7 @@ LL | a.unwrap();
= help: if this value is an `Err`, it will panic
error: used `expect()` on a `Result` value
- --> $DIR/unwrap_expect_used.rs:32:5
+ --> $DIR/unwrap_expect_used.rs:33:5
|
LL | a.expect("Hello world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL | a.expect("Hello world!");
= help: if this value is an `Err`, it will panic
error: used `unwrap_err()` on a `Result` value
- --> $DIR/unwrap_expect_used.rs:33:5
+ --> $DIR/unwrap_expect_used.rs:34:5
|
LL | a.unwrap_err();
| ^^^^^^^^^^^^^^
@@ -41,7 +41,7 @@ LL | a.unwrap_err();
= help: if this value is an `Ok`, it will panic
error: used `expect_err()` on a `Result` value
- --> $DIR/unwrap_expect_used.rs:34:5
+ --> $DIR/unwrap_expect_used.rs:35:5
|
LL | a.expect_err("Hello error!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs
index a0c003f5b..5bea85e66 100644
--- a/src/tools/clippy/tests/ui/unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/unwrap_or.rs
@@ -1,4 +1,5 @@
#![warn(clippy::all, clippy::or_fun_call)]
+#![allow(clippy::unnecessary_literal_unwrap)]
fn main() {
let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr
index c3a7464fd..cf720eaaf 100644
--- a/src/tools/clippy/tests/ui/unwrap_or.stderr
+++ b/src/tools/clippy/tests/ui/unwrap_or.stderr
@@ -1,5 +1,5 @@
error: use of `unwrap_or` followed by a function call
- --> $DIR/unwrap_or.rs:4:47
+ --> $DIR/unwrap_or.rs:5:47
|
LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
@@ -7,7 +7,7 @@ LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string())
= note: `-D clippy::or-fun-call` implied by `-D warnings`
error: use of `unwrap_or` followed by a function call
- --> $DIR/unwrap_or.rs:8:47
+ --> $DIR/unwrap_or.rs:9:47
|
LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
diff --git a/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed b/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
index 59a0ca3f1..08b89a18b 100644
--- a/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
+++ b/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
@@ -2,7 +2,7 @@
#![warn(clippy::unwrap_or_else_default)]
#![allow(dead_code)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)]
/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
fn unwrap_or_else_default() {
diff --git a/src/tools/clippy/tests/ui/unwrap_or_else_default.rs b/src/tools/clippy/tests/ui/unwrap_or_else_default.rs
index 97cafa336..ad2a74490 100644
--- a/src/tools/clippy/tests/ui/unwrap_or_else_default.rs
+++ b/src/tools/clippy/tests/ui/unwrap_or_else_default.rs
@@ -2,7 +2,7 @@
#![warn(clippy::unwrap_or_else_default)]
#![allow(dead_code)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)]
/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
fn unwrap_or_else_default() {
diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh
index 4391499a1..d42043070 100755
--- a/src/tools/clippy/tests/ui/update-all-references.sh
+++ b/src/tools/clippy/tests/ui/update-all-references.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-echo "Please use 'cargo dev bless' instead."
+echo "Please use 'cargo bless' instead."
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 89ea14759..4179f21c5 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![warn(clippy::use_self)]
#![allow(dead_code, unreachable_code)]
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 49e5bcb7e..01a36def9 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![warn(clippy::use_self)]
#![allow(dead_code, unreachable_code)]
diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.rs b/src/tools/clippy/tests/ui/used_underscore_binding.rs
index c672eff1c..879e2e24a 100644
--- a/src/tools/clippy/tests/ui/used_underscore_binding.rs
+++ b/src/tools/clippy/tests/ui/used_underscore_binding.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![feature(rustc_private)]
#![warn(clippy::all)]
#![warn(clippy::used_underscore_binding)]
diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed
index de6660c95..8e77ec444 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.fixed
+++ b/src/tools/clippy/tests/ui/useless_attribute.fixed
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![allow(unused)]
#![warn(clippy::useless_attribute)]
diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs
index 8de4331e8..27498d9bc 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.rs
+++ b/src/tools/clippy/tests/ui/useless_attribute.rs
@@ -1,5 +1,5 @@
//@run-rustfix
-//@aux-build:proc_macro_derive.rs
+//@aux-build:proc_macro_derive.rs:proc-macro
#![allow(unused)]
#![warn(clippy::useless_attribute)]
diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed
index c16caa38f..53d8a5a9f 100644
--- a/src/tools/clippy/tests/ui/useless_conversion.fixed
+++ b/src/tools/clippy/tests/ui/useless_conversion.fixed
@@ -1,7 +1,7 @@
//@run-rustfix
#![deny(clippy::useless_conversion)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::needless_if, clippy::unnecessary_wraps)]
fn test_generic<T: Copy>(val: T) -> T {
let _ = val;
@@ -155,6 +155,49 @@ fn main() {
let _ = vec![s4, s4, s4].into_iter();
}
+#[allow(dead_code)]
+fn issue11065_fp() {
+ use std::option::IntoIter;
+ fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
+
+ macro_rules! x {
+ ($e:expr) => {
+ takes_into_iter($e);
+ let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here
+ };
+ }
+ x!(Some(5).into_iter());
+}
+
+#[allow(dead_code)]
+fn explicit_into_iter_fn_arg() {
+ fn a<T>(_: T) {}
+ fn b<T: IntoIterator<Item = i32>>(_: T) {}
+ fn c(_: impl IntoIterator<Item = i32>) {}
+ fn d<T>(_: T)
+ where
+ T: IntoIterator<Item = i32>,
+ {
+ }
+ fn f(_: std::vec::IntoIter<i32>) {}
+
+ a(vec![1, 2].into_iter());
+ b(vec![1, 2]);
+ c(vec![1, 2]);
+ d(vec![1, 2]);
+ b([&1, &2, &3].into_iter().cloned());
+
+ b(vec![1, 2]);
+ b(vec![1, 2]);
+
+ macro_rules! macro_generated {
+ () => {
+ vec![1, 2].into_iter()
+ };
+ }
+ b(macro_generated!());
+}
+
#[derive(Copy, Clone)]
struct Foo<const C: char>;
diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs
index c75a2bce4..51ba49873 100644
--- a/src/tools/clippy/tests/ui/useless_conversion.rs
+++ b/src/tools/clippy/tests/ui/useless_conversion.rs
@@ -1,7 +1,7 @@
//@run-rustfix
#![deny(clippy::useless_conversion)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::needless_if, clippy::unnecessary_wraps)]
fn test_generic<T: Copy>(val: T) -> T {
let _ = T::from(val);
@@ -155,6 +155,49 @@ fn main() {
let _ = vec![s4, s4, s4].into_iter().into_iter();
}
+#[allow(dead_code)]
+fn issue11065_fp() {
+ use std::option::IntoIter;
+ fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
+
+ macro_rules! x {
+ ($e:expr) => {
+ takes_into_iter($e);
+ let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here
+ };
+ }
+ x!(Some(5).into_iter());
+}
+
+#[allow(dead_code)]
+fn explicit_into_iter_fn_arg() {
+ fn a<T>(_: T) {}
+ fn b<T: IntoIterator<Item = i32>>(_: T) {}
+ fn c(_: impl IntoIterator<Item = i32>) {}
+ fn d<T>(_: T)
+ where
+ T: IntoIterator<Item = i32>,
+ {
+ }
+ fn f(_: std::vec::IntoIter<i32>) {}
+
+ a(vec![1, 2].into_iter());
+ b(vec![1, 2].into_iter());
+ c(vec![1, 2].into_iter());
+ d(vec![1, 2].into_iter());
+ b([&1, &2, &3].into_iter().cloned());
+
+ b(vec![1, 2].into_iter().into_iter());
+ b(vec![1, 2].into_iter().into_iter().into_iter());
+
+ macro_rules! macro_generated {
+ () => {
+ vec![1, 2].into_iter()
+ };
+ }
+ b(macro_generated!());
+}
+
#[derive(Copy, Clone)]
struct Foo<const C: char>;
diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr
index 4dca3aac5..6f7dc01d2 100644
--- a/src/tools/clippy/tests/ui/useless_conversion.stderr
+++ b/src/tools/clippy/tests/ui/useless_conversion.stderr
@@ -118,5 +118,65 @@ error: useless conversion to the same type: `std::vec::IntoIter<Foo<'a'>>`
LL | let _ = vec![s4, s4, s4].into_iter().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()`
-error: aborting due to 19 previous errors
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+ --> $DIR/useless_conversion.rs:185:7
+ |
+LL | b(vec![1, 2].into_iter());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
+ |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+ --> $DIR/useless_conversion.rs:175:13
+ |
+LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+ --> $DIR/useless_conversion.rs:186:7
+ |
+LL | c(vec![1, 2].into_iter());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
+ |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+ --> $DIR/useless_conversion.rs:176:18
+ |
+LL | fn c(_: impl IntoIterator<Item = i32>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+ --> $DIR/useless_conversion.rs:187:7
+ |
+LL | d(vec![1, 2].into_iter());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
+ |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+ --> $DIR/useless_conversion.rs:179:12
+ |
+LL | T: IntoIterator<Item = i32>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+ --> $DIR/useless_conversion.rs:190:7
+ |
+LL | b(vec![1, 2].into_iter().into_iter());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]`
+ |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+ --> $DIR/useless_conversion.rs:175:13
+ |
+LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+ --> $DIR/useless_conversion.rs:191:7
+ |
+LL | b(vec![1, 2].into_iter().into_iter().into_iter());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]`
+ |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+ --> $DIR/useless_conversion.rs:175:13
+ |
+LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 24 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 4acf5b5fa..ec0512ce2 100644
--- a/src/tools/clippy/tests/ui/useless_conversion_try.rs
+++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs
@@ -1,4 +1,5 @@
#![deny(clippy::useless_conversion)]
+#![allow(clippy::needless_if)]
fn test_generic<T: Copy>(val: T) -> T {
let _ = T::try_from(val).unwrap();
diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr
index 9aef9dda6..54189f8d2 100644
--- a/src/tools/clippy/tests/ui/useless_conversion_try.stderr
+++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr
@@ -1,5 +1,5 @@
error: useless conversion to the same type: `T`
- --> $DIR/useless_conversion_try.rs:4:13
+ --> $DIR/useless_conversion_try.rs:5:13
|
LL | let _ = T::try_from(val).unwrap();
| ^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: useless conversion to the same type: `T`
- --> $DIR/useless_conversion_try.rs:5:5
+ --> $DIR/useless_conversion_try.rs:6:5
|
LL | val.try_into().unwrap()
| ^^^^^^^^^^^^^^
@@ -20,7 +20,7 @@ LL | val.try_into().unwrap()
= help: consider removing `.try_into()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:27:21
+ --> $DIR/useless_conversion_try.rs:28:21
|
LL | let _: String = "foo".to_string().try_into().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap();
= help: consider removing `.try_into()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:28:21
+ --> $DIR/useless_conversion_try.rs:29:21
|
LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap();
= help: consider removing `TryFrom::try_from()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:29:13
+ --> $DIR/useless_conversion_try.rs:30:13
|
LL | let _ = String::try_from("foo".to_string()).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap();
= help: consider removing `String::try_from()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:30:13
+ --> $DIR/useless_conversion_try.rs:31:13
|
LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
= help: consider removing `String::try_from()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:31:21
+ --> $DIR/useless_conversion_try.rs:32:21
|
LL | let _: String = format!("Hello {}", "world").try_into().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap();
= help: consider removing `.try_into()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:32:21
+ --> $DIR/useless_conversion_try.rs:33:21
|
LL | let _: String = String::new().try_into().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -68,7 +68,7 @@ LL | let _: String = String::new().try_into().unwrap();
= help: consider removing `.try_into()`
error: useless conversion to the same type: `std::string::String`
- --> $DIR/useless_conversion_try.rs:33:27
+ --> $DIR/useless_conversion_try.rs:34:27
|
LL | let _: String = match String::from("_").try_into() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed
index d77a4dd8e..fcdc917c1 100644
--- a/src/tools/clippy/tests/ui/vec.fixed
+++ b/src/tools/clippy/tests/ui/vec.fixed
@@ -1,9 +1,12 @@
//@run-rustfix
#![warn(clippy::useless_vec)]
-#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)]
+#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)]
-#[derive(Debug)]
-struct NonCopy;
+use std::rc::Rc;
+
+struct StructWithVec {
+ _x: Vec<i32>,
+}
fn on_slice(_: &[u8]) {}
@@ -60,14 +63,6 @@ fn main() {
on_mut_slice(&mut vec![2; line.length]);
on_mut_slice(&mut vec![2; line.length()]);
- for a in &[1, 2, 3] {
- println!("{:?}", a);
- }
-
- for a in vec![NonCopy, NonCopy] {
- println!("{:?}", a);
- }
-
on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
@@ -75,4 +70,69 @@ fn main() {
for a in vec![1; 201] {
println!("{:?}", a);
}
+
+ // https://github.com/rust-lang/rust-clippy/issues/2262#issuecomment-783979246
+ let _x: i32 = [1, 2, 3].iter().sum();
+
+ // Do lint
+ let mut x = [1, 2, 3];
+ x.fill(123);
+ dbg!(x[0]);
+ dbg!(x.len());
+ dbg!(x.iter().sum::<i32>());
+
+ let _x: &[i32] = &[1, 2, 3];
+
+ for _ in [1, 2, 3] {}
+
+ // Don't lint
+ let x = vec![1, 2, 3];
+ let _v: Vec<i32> = x;
+
+ let x = vec![1, 2, 3];
+ let _s = StructWithVec { _x: x };
+
+ // Explicit type annotation would make the change to [1, 2, 3]
+ // a compile error.
+ let _x: Vec<i32> = vec![1, 2, 3];
+
+ // Calling a Vec method through a mutable reference
+ let mut x = vec![1, 2, 3];
+ let re = &mut x;
+ re.push(4);
+
+ // Comparing arrays whose length is not equal is a compile error
+ let x = vec![1, 2, 3];
+ let y = vec![1, 2, 3, 4];
+ dbg!(x == y);
+
+ // Non-copy types
+ let _x = vec![String::new(); 10];
+ #[allow(clippy::rc_clone_in_vec_init)]
+ let _x = vec![Rc::new(1); 10];
+
+ // Too large
+ let _x = vec![1; 201];
+}
+
+#[clippy::msrv = "1.53"]
+fn above() {
+ for a in [1, 2, 3] {
+ let _: usize = a;
+ }
+
+ for a in [String::new(), String::new()] {
+ let _: String = a;
+ }
+}
+
+#[clippy::msrv = "1.52"]
+fn below() {
+ for a in vec![1, 2, 3] {
+ let _: usize = a;
+ }
+
+ for a in vec![String::new(), String::new()] {
+ let _: String = a;
+ }
}
diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs
index dfed3a29a..0404d8cdb 100644
--- a/src/tools/clippy/tests/ui/vec.rs
+++ b/src/tools/clippy/tests/ui/vec.rs
@@ -1,9 +1,12 @@
//@run-rustfix
#![warn(clippy::useless_vec)]
-#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)]
+#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)]
-#[derive(Debug)]
-struct NonCopy;
+use std::rc::Rc;
+
+struct StructWithVec {
+ _x: Vec<i32>,
+}
fn on_slice(_: &[u8]) {}
@@ -60,14 +63,6 @@ fn main() {
on_mut_slice(&mut vec![2; line.length]);
on_mut_slice(&mut vec![2; line.length()]);
- for a in vec![1, 2, 3] {
- println!("{:?}", a);
- }
-
- for a in vec![NonCopy, NonCopy] {
- println!("{:?}", a);
- }
-
on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
@@ -75,4 +70,69 @@ fn main() {
for a in vec![1; 201] {
println!("{:?}", a);
}
+
+ // https://github.com/rust-lang/rust-clippy/issues/2262#issuecomment-783979246
+ let _x: i32 = vec![1, 2, 3].iter().sum();
+
+ // Do lint
+ let mut x = vec![1, 2, 3];
+ x.fill(123);
+ dbg!(x[0]);
+ dbg!(x.len());
+ dbg!(x.iter().sum::<i32>());
+
+ let _x: &[i32] = &vec![1, 2, 3];
+
+ for _ in vec![1, 2, 3] {}
+
+ // Don't lint
+ let x = vec![1, 2, 3];
+ let _v: Vec<i32> = x;
+
+ let x = vec![1, 2, 3];
+ let _s = StructWithVec { _x: x };
+
+ // Explicit type annotation would make the change to [1, 2, 3]
+ // a compile error.
+ let _x: Vec<i32> = vec![1, 2, 3];
+
+ // Calling a Vec method through a mutable reference
+ let mut x = vec![1, 2, 3];
+ let re = &mut x;
+ re.push(4);
+
+ // Comparing arrays whose length is not equal is a compile error
+ let x = vec![1, 2, 3];
+ let y = vec![1, 2, 3, 4];
+ dbg!(x == y);
+
+ // Non-copy types
+ let _x = vec![String::new(); 10];
+ #[allow(clippy::rc_clone_in_vec_init)]
+ let _x = vec![Rc::new(1); 10];
+
+ // Too large
+ let _x = vec![1; 201];
+}
+
+#[clippy::msrv = "1.53"]
+fn above() {
+ for a in vec![1, 2, 3] {
+ let _: usize = a;
+ }
+
+ for a in vec![String::new(), String::new()] {
+ let _: String = a;
+ }
+}
+
+#[clippy::msrv = "1.52"]
+fn below() {
+ for a in vec![1, 2, 3] {
+ let _: usize = a;
+ }
+
+ for a in vec![String::new(), String::new()] {
+ let _: String = a;
+ }
}
diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr
index 7d1de05a5..33d565b2d 100644
--- a/src/tools/clippy/tests/ui/vec.stderr
+++ b/src/tools/clippy/tests/ui/vec.stderr
@@ -1,5 +1,5 @@
error: useless use of `vec!`
- --> $DIR/vec.rs:28:14
+ --> $DIR/vec.rs:31:14
|
LL | on_slice(&vec![]);
| ^^^^^^^ help: you can use a slice directly: `&[]`
@@ -7,64 +7,94 @@ LL | on_slice(&vec![]);
= note: `-D clippy::useless-vec` implied by `-D warnings`
error: useless use of `vec!`
- --> $DIR/vec.rs:30:18
+ --> $DIR/vec.rs:33:18
|
LL | on_mut_slice(&mut vec![]);
| ^^^^^^^^^^^ help: you can use a slice directly: `&mut []`
error: useless use of `vec!`
- --> $DIR/vec.rs:32:14
+ --> $DIR/vec.rs:35:14
|
LL | on_slice(&vec![1, 2]);
| ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:34:18
+ --> $DIR/vec.rs:37:18
|
LL | on_mut_slice(&mut vec![1, 2]);
| ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:36:14
+ --> $DIR/vec.rs:39:14
|
LL | on_slice(&vec![1, 2]);
| ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:38:18
+ --> $DIR/vec.rs:41:18
|
LL | on_mut_slice(&mut vec![1, 2]);
| ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:40:14
+ --> $DIR/vec.rs:43:14
|
LL | on_slice(&vec!(1, 2));
| ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:42:18
+ --> $DIR/vec.rs:45:18
|
LL | on_mut_slice(&mut vec![1, 2]);
| ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:44:14
+ --> $DIR/vec.rs:47:14
|
LL | on_slice(&vec![1; 2]);
| ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:46:18
+ --> $DIR/vec.rs:49:18
|
LL | on_mut_slice(&mut vec![1; 2]);
| ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]`
error: useless use of `vec!`
- --> $DIR/vec.rs:63:14
+ --> $DIR/vec.rs:75:19
+ |
+LL | let _x: i32 = vec![1, 2, 3].iter().sum();
+ | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:78:17
+ |
+LL | let mut x = vec![1, 2, 3];
+ | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:84:22
+ |
+LL | let _x: &[i32] = &vec![1, 2, 3];
+ | ^^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:86:14
+ |
+LL | for _ in vec![1, 2, 3] {}
+ | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:120:14
|
LL | for a in vec![1, 2, 3] {
- | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
+ | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:124:14
+ |
+LL | for a in vec![String::new(), String::new()] {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]`
-error: aborting due to 11 previous errors
+error: aborting due to 16 previous errors
diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs
index a9a4a0f5a..99c3f468f 100644
--- a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs
+++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs
@@ -23,12 +23,6 @@ fn main() {
let b = &1 as &dyn Debug;
ptr::eq(a, b);
- let a: Rc<dyn Debug> = Rc::new(1);
- Rc::ptr_eq(&a, &a);
-
- let a: Arc<dyn Debug> = Arc::new(1);
- Arc::ptr_eq(&a, &a);
-
// These should be fine:
let a = &1;
ptr::eq(a, a);
@@ -39,6 +33,12 @@ fn main() {
let a = Arc::new(1);
Arc::ptr_eq(&a, &a);
+ let a: Rc<dyn Debug> = Rc::new(1);
+ Rc::ptr_eq(&a, &a);
+
+ let a: Arc<dyn Debug> = Arc::new(1);
+ Arc::ptr_eq(&a, &a);
+
let a: &[u8] = b"";
ptr::eq(a, a);
}
diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
index 14748f583..7b866d274 100644
--- a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
+++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
@@ -63,21 +63,5 @@ LL | ptr::eq(a, b);
|
= help: consider extracting and comparing data pointers only
-error: comparing trait object pointers compares a non-unique vtable address
- --> $DIR/vtable_address_comparisons.rs:27:5
- |
-LL | Rc::ptr_eq(&a, &a);
- | ^^^^^^^^^^^^^^^^^^
- |
- = help: consider extracting and comparing data pointers only
-
-error: comparing trait object pointers compares a non-unique vtable address
- --> $DIR/vtable_address_comparisons.rs:30:5
- |
-LL | Arc::ptr_eq(&a, &a);
- | ^^^^^^^^^^^^^^^^^^^
- |
- = help: consider extracting and comparing data pointers only
-
-error: aborting due to 10 previous errors
+error: aborting due to 8 previous errors
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
index c2f216a89..41a380ab8 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
@@ -6,7 +6,9 @@
clippy::manual_find,
clippy::never_loop,
clippy::redundant_closure_call,
- clippy::uninlined_format_args
+ clippy::single_range_in_vec_init,
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
fn base() {
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
index 971bd5f0c..4c6433880 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
@@ -6,7 +6,9 @@
clippy::manual_find,
clippy::never_loop,
clippy::redundant_closure_call,
- clippy::uninlined_format_args
+ clippy::single_range_in_vec_init,
+ clippy::uninlined_format_args,
+ clippy::useless_vec
)]
fn base() {
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
index 4d9866619..3236765e1 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
@@ -1,5 +1,5 @@
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:14:5
+ --> $DIR/while_let_on_iterator.rs:16:5
|
LL | while let Option::Some(x) = iter.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
@@ -7,151 +7,151 @@ LL | while let Option::Some(x) = iter.next() {
= note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:19:5
+ --> $DIR/while_let_on_iterator.rs:21:5
|
LL | while let Some(x) = iter.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:24:5
+ --> $DIR/while_let_on_iterator.rs:26:5
|
LL | while let Some(_) = iter.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:100:9
+ --> $DIR/while_let_on_iterator.rs:102:9
|
LL | while let Some([..]) = it.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:107:9
+ --> $DIR/while_let_on_iterator.rs:109:9
|
LL | while let Some([_x]) = it.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:120:9
+ --> $DIR/while_let_on_iterator.rs:122:9
|
LL | while let Some(x @ [_]) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:140:9
+ --> $DIR/while_let_on_iterator.rs:142:9
|
LL | while let Some(_) = y.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:197:9
+ --> $DIR/while_let_on_iterator.rs:199:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:208:5
+ --> $DIR/while_let_on_iterator.rs:210:5
|
LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:210:9
+ --> $DIR/while_let_on_iterator.rs:212:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:219:9
+ --> $DIR/while_let_on_iterator.rs:221:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:228:9
+ --> $DIR/while_let_on_iterator.rs:230:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:245:9
+ --> $DIR/while_let_on_iterator.rs:247:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:260:13
+ --> $DIR/while_let_on_iterator.rs:262:13
|
LL | while let Some(i) = self.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:292:13
+ --> $DIR/while_let_on_iterator.rs:294:13
|
LL | while let Some(i) = self.0.0.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:321:5
+ --> $DIR/while_let_on_iterator.rs:323:5
|
LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:333:9
+ --> $DIR/while_let_on_iterator.rs:335:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:347:5
+ --> $DIR/while_let_on_iterator.rs:349:5
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:358:5
+ --> $DIR/while_let_on_iterator.rs:360:5
|
LL | while let Some(x) = it.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:393:5
+ --> $DIR/while_let_on_iterator.rs:395:5
|
LL | while let Some(x) = s.x.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:400:5
+ --> $DIR/while_let_on_iterator.rs:402:5
|
LL | while let Some(x) = x[0].next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:408:9
+ --> $DIR/while_let_on_iterator.rs:410:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:418:9
+ --> $DIR/while_let_on_iterator.rs:420:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:428:9
+ --> $DIR/while_let_on_iterator.rs:430:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:438:9
+ --> $DIR/while_let_on_iterator.rs:440:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:448:5
+ --> $DIR/while_let_on_iterator.rs:450:5
|
LL | while let Some(..) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed
index bd845361f..2961b062e 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed
@@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
use wildcard_imports_helper::{ExternA, extern_foo};
use std::io::prelude::*;
+use wildcard_imports_helper::extern_prelude::v1::*;
use wildcard_imports_helper::prelude::v1::*;
struct ReadFoo;
@@ -81,6 +82,7 @@ fn main() {
let _ = inner_struct_mod::C;
let _ = ExternA;
let _ = PreludeModAnywhere;
+ let _ = ExternPreludeModAnywhere;
double_struct_import_test!();
double_struct_import_test!();
@@ -209,7 +211,7 @@ mod super_imports {
}
mod use_explicit_should_be_replaced {
- use super_imports::foofoo;
+ use crate::super_imports::foofoo;
fn with_explicit() {
let _ = foofoo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs
index fb51f7bdf..28508a253 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.rs
+++ b/src/tools/clippy/tests/ui/wildcard_imports.rs
@@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*;
use wildcard_imports_helper::*;
use std::io::prelude::*;
+use wildcard_imports_helper::extern_prelude::v1::*;
use wildcard_imports_helper::prelude::v1::*;
struct ReadFoo;
@@ -81,6 +82,7 @@ fn main() {
let _ = inner_struct_mod::C;
let _ = ExternA;
let _ = PreludeModAnywhere;
+ let _ = ExternPreludeModAnywhere;
double_struct_import_test!();
double_struct_import_test!();
@@ -210,7 +212,7 @@ mod super_imports {
}
mod use_explicit_should_be_replaced {
- use super_imports::*;
+ use crate::super_imports::*;
fn with_explicit() {
let _ = foofoo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr
index 6b469cdfc..c96b3041a 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr
@@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:95:13
+ --> $DIR/wildcard_imports.rs:97:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:101:75
+ --> $DIR/wildcard_imports.rs:103: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:102:13
+ --> $DIR/wildcard_imports.rs:104:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:113:20
+ --> $DIR/wildcard_imports.rs:115:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:113:30
+ --> $DIR/wildcard_imports.rs:115:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:120:13
+ --> $DIR/wildcard_imports.rs:122:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:149:9
+ --> $DIR/wildcard_imports.rs:151: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:158:9
+ --> $DIR/wildcard_imports.rs:160:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:159:9
+ --> $DIR/wildcard_imports.rs:161:9
|
LL | use crate:: fn_mod::
| _________^
@@ -93,37 +93,37 @@ LL | | *;
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:170:13
+ --> $DIR/wildcard_imports.rs:172:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:205:17
+ --> $DIR/wildcard_imports.rs:207:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:213:13
+ --> $DIR/wildcard_imports.rs:215:13
|
-LL | use super_imports::*;
- | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
+LL | use crate::super_imports::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:222:17
+ --> $DIR/wildcard_imports.rs:224:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:231:13
+ --> $DIR/wildcard_imports.rs:233:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:239:13
+ --> $DIR/wildcard_imports.rs:241:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs
index 55a11daa1..805127e27 100644
--- a/src/tools/clippy/tests/ui/write_literal_2.rs
+++ b/src/tools/clippy/tests/ui/write_literal_2.rs
@@ -1,5 +1,5 @@
#![allow(unused_must_use)]
-#![warn(clippy::write_literal)]
+#![warn(clippy::needless_raw_strings, clippy::write_literal)]
use std::io::Write;
diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr
index d5956db9f..18591250a 100644
--- a/src/tools/clippy/tests/ui/write_literal_2.stderr
+++ b/src/tools/clippy/tests/ui/write_literal_2.stderr
@@ -1,3 +1,11 @@
+error: unnecessary raw string literal
+ --> $DIR/write_literal_2.rs:10:24
+ |
+LL | writeln!(v, r"{}", r"{hello}");
+ | ^^^^^^^^^^ help: try: `"{hello}"`
+ |
+ = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
+
error: literal with an empty format string
--> $DIR/write_literal_2.rs:9:23
|
@@ -87,7 +95,7 @@ LL | "1", "2", "3",
help: try this
|
LL ~ "some 1/
-LL ~ {} / {}", "2", "3",
+LL ~ {} // {}", "2", "3",
|
error: literal with an empty format string
@@ -98,7 +106,7 @@ LL | "1", "2", "3",
|
help: try this
|
-LL ~ 2 / {}",
+LL ~ 2 // {}",
LL ~ "1", "3",
|
@@ -110,43 +118,43 @@ LL | "1", "2", "3",
|
help: try this
|
-LL ~ {} / 3",
+LL ~ {} // 3",
LL ~ "1", "2",
|
error: literal with an empty format string
--> $DIR/write_literal_2.rs:27:23
|
-LL | writeln!(v, "{}", "/");
+LL | writeln!(v, "{}", "//");
| ^^^^
|
help: try this
|
-LL - writeln!(v, "{}", "/");
-LL + writeln!(v, "/");
+LL - writeln!(v, "{}", "//");
+LL + writeln!(v, "//");
|
error: literal with an empty format string
--> $DIR/write_literal_2.rs:28:24
|
-LL | writeln!(v, r"{}", "/");
+LL | writeln!(v, r"{}", "//");
| ^^^^
|
help: try this
|
-LL - writeln!(v, r"{}", "/");
+LL - writeln!(v, r"{}", "//");
LL + writeln!(v, r"/");
|
error: literal with an empty format string
--> $DIR/write_literal_2.rs:29:26
|
-LL | writeln!(v, r#"{}"#, "/");
+LL | writeln!(v, r#"{}"#, "//");
| ^^^^
|
help: try this
|
-LL - writeln!(v, r#"{}"#, "/");
+LL - writeln!(v, r#"{}"#, "//");
LL + writeln!(v, r#"/"#);
|
@@ -159,7 +167,7 @@ LL | writeln!(v, "{}", r"/");
help: try this
|
LL - writeln!(v, "{}", r"/");
-LL + writeln!(v, "/");
+LL + writeln!(v, "//");
|
error: literal with an empty format string
@@ -186,5 +194,5 @@ error: literal with an empty format string
LL | writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
| ^^^
-error: aborting due to 17 previous errors
+error: aborting due to 18 previous errors
diff --git a/src/tools/clippy/tests/ui/write_with_newline.fixed b/src/tools/clippy/tests/ui/write_with_newline.fixed
new file mode 100644
index 000000000..0a10e526a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_with_newline.fixed
@@ -0,0 +1,63 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+// //@run-rustfix
+
+#![allow(clippy::write_literal)]
+#![warn(clippy::write_with_newline)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ // These should fail
+ writeln!(v, "Hello");
+ writeln!(v, "Hello {}", "world");
+ writeln!(v, "Hello {} {}", "world", "#2");
+ writeln!(v, "{}", 1265);
+ writeln!(v);
+
+ // These should be fine
+ write!(v, "");
+ write!(v, "Hello");
+ writeln!(v, "Hello");
+ writeln!(v, "Hello\n");
+ writeln!(v, "Hello {}\n", "world");
+ write!(v, "Issue\n{}", 1265);
+ write!(v, "{}", 1265);
+ write!(v, "\n{}", 1275);
+ write!(v, "\n\n");
+ write!(v, "like eof\n\n");
+ write!(v, "Hello {} {}\n\n", "world", "#2");
+ writeln!(v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ writeln!(v, "\nbla\n\n"); // #3126
+
+ // Escaping
+ write!(v, "\\n"); // #3514
+ writeln!(v, "\\"); // should fail
+ write!(v, "\\\\n");
+
+ // Raw strings
+ write!(v, r"\n"); // #3778
+
+ // Literal newlines should also fail
+ writeln!(
+ v
+ );
+ writeln!(
+ v
+ );
+
+ // Don't warn on CRLF (#4208)
+ write!(v, "\r\n");
+ write!(v, "foo\r\n");
+ writeln!(v, "\\r"); // warns
+ write!(v, "foo\rbar\n");
+
+ // Ignore expanded format strings
+ macro_rules! newline {
+ () => {
+ "\n"
+ };
+ }
+ write!(v, newline!());
+}
diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr
index 9035275b2..03a18a4dc 100644
--- a/src/tools/clippy/tests/ui/write_with_newline.stderr
+++ b/src/tools/clippy/tests/ui/write_with_newline.stderr
@@ -62,13 +62,13 @@ LL + writeln!(v);
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:36:5
|
-LL | write!(v, "//n"); // should fail
+LL | write!(v, "///n"); // should fail
| ^^^^^^^^^^^^^^^^^
|
help: use `writeln!` instead
|
-LL - write!(v, "//n"); // should fail
-LL + writeln!(v, "/"); // should fail
+LL - write!(v, "///n"); // should fail
+LL + writeln!(v, "//"); // should fail
|
error: using `write!()` with a format string that ends in a single newline
@@ -106,13 +106,13 @@ LL ~ v
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:57:5
|
-LL | write!(v, "/r/n");
+LL | write!(v, "//r/n");
| ^^^^^^^^^^^^^^^^^^
|
help: use `writeln!` instead
|
-LL - write!(v, "/r/n");
-LL + writeln!(v, "/r");
+LL - write!(v, "//r/n");
+LL + writeln!(v, "//r");
|
error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs
index c9cbc5054..699ab2be1 100644
--- a/src/tools/clippy/tests/workspace.rs
+++ b/src/tools/clippy/tests/workspace.rs
@@ -7,6 +7,46 @@ use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE};
mod test_utils;
#[test]
+fn test_module_style_with_dep_in_subdir() {
+ if IS_RUSTC_TEST_SUITE {
+ return;
+ }
+ let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ let target_dir = root.join("target").join("workspace_test");
+ let cwd = root.join("tests/workspace_test");
+
+ // Make sure we start with a clean state
+ Command::new("cargo")
+ .current_dir(&cwd)
+ .env("CARGO_TARGET_DIR", &target_dir)
+ .arg("clean")
+ .args(["-p", "pass-no-mod-with-dep-in-subdir"])
+ .args(["-p", "pass-mod-with-dep-in-subdir"])
+ .output()
+ .unwrap();
+
+ // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887)
+ // `mod.rs` checks should not be applied to crate dependencies
+ // located in the subdirectory of workspace
+ let output = Command::new(&*CARGO_CLIPPY_PATH)
+ .current_dir(&cwd)
+ .env("CARGO_INCREMENTAL", "0")
+ .env("CARGO_TARGET_DIR", &target_dir)
+ .arg("clippy")
+ .args(["-p", "pass-no-mod-with-dep-in-subdir"])
+ .args(["-p", "pass-mod-with-dep-in-subdir"])
+ .arg("--")
+ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+ .output()
+ .unwrap();
+
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+ assert!(output.status.success());
+}
+
+#[test]
fn test_no_deps_ignores_path_deps_in_workspaces() {
if IS_RUSTC_TEST_SUITE {
return;
diff --git a/src/tools/clippy/tests/workspace_test/Cargo.toml b/src/tools/clippy/tests/workspace_test/Cargo.toml
index bf5b4ca52..d24b278cc 100644
--- a/src/tools/clippy/tests/workspace_test/Cargo.toml
+++ b/src/tools/clippy/tests/workspace_test/Cargo.toml
@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2018"
[workspace]
-members = ["subcrate"]
+members = ["subcrate", "module_style/pass_no_mod_with_dep_in_subdir", "module_style/pass_mod_with_dep_in_subdir"]
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/Cargo.toml b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/Cargo.toml
new file mode 100644
index 000000000..15dcde4e3
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "pass-mod-with-dep-in-subdir"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dep-no-mod = { path = "dep_no_mod"}
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/Cargo.toml b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/Cargo.toml
new file mode 100644
index 000000000..55569bc25
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "dep-no-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo.rs
new file mode 100644
index 000000000..7b0966a45
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo.rs
@@ -0,0 +1,2 @@
+pub mod hello;
+pub struct Thing;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo/hello.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo/hello.rs
new file mode 100644
index 000000000..99940dce5
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/foo/hello.rs
@@ -0,0 +1 @@
+pub struct Hello;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/lib.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/lib.rs
new file mode 100644
index 000000000..c62f9acbf
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/dep_no_mod/src/lib.rs
@@ -0,0 +1,5 @@
+pub mod foo;
+
+pub fn foo() {
+ let _ = foo::Thing;
+}
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/bad/mod.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/bad/mod.rs
new file mode 100644
index 000000000..f19ab10d5
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/bad/mod.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/main.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/main.rs
new file mode 100644
index 000000000..5cb4795e9
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/main.rs
@@ -0,0 +1,13 @@
+#![deny(clippy::self_named_module_files)]
+
+mod bad;
+mod more;
+extern crate dep_no_mod;
+
+fn main() {
+ let _ = bad::Thing;
+ let _ = more::foo::Foo;
+ let _ = more::inner::Inner;
+ let _ = dep_no_mod::foo::Thing;
+ let _ = dep_no_mod::foo::hello::Hello;
+}
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/foo.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/foo.rs
new file mode 100644
index 000000000..4a835673a
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/foo.rs
@@ -0,0 +1 @@
+pub struct Foo;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/inner/mod.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/inner/mod.rs
new file mode 100644
index 000000000..aa84f78cc
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/inner/mod.rs
@@ -0,0 +1 @@
+pub struct Inner;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/mod.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/mod.rs
new file mode 100644
index 000000000..d79569f78
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_mod_with_dep_in_subdir/src/more/mod.rs
@@ -0,0 +1,2 @@
+pub mod foo;
+pub mod inner;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/Cargo.toml b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/Cargo.toml
new file mode 100644
index 000000000..060cb18dc
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "pass-no-mod-with-dep-in-subdir"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dep-with-mod = { path = "dep_with_mod"}
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/Cargo.toml b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/Cargo.toml
new file mode 100644
index 000000000..b25725cd5
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "dep-with-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/lib.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/lib.rs
new file mode 100644
index 000000000..4647424f2
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/lib.rs
@@ -0,0 +1,7 @@
+pub mod with_mod;
+
+pub fn foo() {
+ let _ = with_mod::Thing;
+ let _ = with_mod::inner::stuff::Inner;
+ let _ = with_mod::inner::stuff::most::Snarks;
+}
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner.rs
new file mode 100644
index 000000000..91cd540a2
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner.rs
@@ -0,0 +1 @@
+pub mod stuff;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff.rs
new file mode 100644
index 000000000..7713fa9d3
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff.rs
@@ -0,0 +1,3 @@
+pub mod most;
+
+pub struct Inner;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff/most.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff/most.rs
new file mode 100644
index 000000000..5a5eaf967
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/inner/stuff/most.rs
@@ -0,0 +1 @@
+pub struct Snarks;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/mod.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/mod.rs
new file mode 100644
index 000000000..a12734db7
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/dep_with_mod/src/with_mod/mod.rs
@@ -0,0 +1,3 @@
+pub mod inner;
+
+pub struct Thing;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/good.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/good.rs
new file mode 100644
index 000000000..f19ab10d5
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/good.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/main.rs b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/main.rs
new file mode 100644
index 000000000..42eb99cd7
--- /dev/null
+++ b/src/tools/clippy/tests/workspace_test/module_style/pass_no_mod_with_dep_in_subdir/src/main.rs
@@ -0,0 +1,9 @@
+#![deny(clippy::mod_module_files)]
+
+mod good;
+pub use dep_with_mod::with_mod::Thing;
+
+fn main() {
+ let _ = good::Thing;
+ let _ = dep_with_mod::with_mod::Thing;
+}
diff --git a/src/tools/clippy/util/etc/vscode-tasks.json b/src/tools/clippy/util/etc/vscode-tasks.json
index ab98f9b41..38e31b337 100644
--- a/src/tools/clippy/util/etc/vscode-tasks.json
+++ b/src/tools/clippy/util/etc/vscode-tasks.json
@@ -47,9 +47,9 @@
"group": "test"
},
{
- "label": "cargo dev bless",
+ "label": "bless ui tests",
"type": "shell",
- "command": "cargo dev bless",
+ "command": "cargo bless",
"problemMatcher": [],
"group": "none"
}
diff --git a/src/tools/clippy/util/versions.py b/src/tools/clippy/util/versions.py
index 0cfa007d1..c041fc606 100755
--- a/src/tools/clippy/util/versions.py
+++ b/src/tools/clippy/util/versions.py
@@ -1,24 +1,27 @@
#!/usr/bin/env python
import json
+import logging as log
import os
import sys
-import logging as log
-log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s')
+
+log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s")
def key(v):
- if v == 'master':
- return float('inf')
- if v == 'stable':
+ if v == "master":
+ return float("inf")
+ if v == "stable":
return sys.maxsize
- if v == 'beta':
+ if v == "beta":
return sys.maxsize - 1
+ if v == "pre-1.29.0":
+ return -1
- v = v.replace('v', '').replace('rust-', '')
+ v = v.replace("rust-", "")
s = 0
- for i, val in enumerate(v.split('.')[::-1]):
+ for i, val in enumerate(v.split(".")[::-1]):
s += int(val) * 100**i
return s
@@ -31,7 +34,11 @@ def main():
outdir = sys.argv[1]
versions = [
- dir for dir in os.listdir(outdir) if not dir.startswith(".") and os.path.isdir(os.path.join(outdir, dir))
+ dir
+ for dir in os.listdir(outdir)
+ if not dir.startswith(".")
+ and not dir.startswith("v")
+ and os.path.isdir(os.path.join(outdir, dir))
]
versions.sort(key=key)
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index f796c8987..7c17e92d0 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -66,6 +66,7 @@ string_enum! {
JsDocTest => "js-doc-test",
MirOpt => "mir-opt",
Assembly => "assembly",
+ RunCoverage => "run-coverage",
}
}
@@ -106,8 +107,8 @@ string_enum! {
#[derive(Clone, Debug, PartialEq)]
pub enum CompareMode {
Polonius => "polonius",
- Chalk => "chalk",
NextSolver => "next-solver",
+ NextSolverCoherence => "next-solver-coherence",
SplitDwarf => "split-dwarf",
SplitDwarfSingle => "split-dwarf-single",
}
@@ -130,6 +131,15 @@ pub enum PanicStrategy {
Abort,
}
+impl PanicStrategy {
+ pub(crate) fn for_miropt_test_tools(&self) -> miropt_test_tools::PanicStrategy {
+ match self {
+ PanicStrategy::Unwind => miropt_test_tools::PanicStrategy::Unwind,
+ PanicStrategy::Abort => miropt_test_tools::PanicStrategy::Abort,
+ }
+ }
+}
+
/// Configuration for compiletest
#[derive(Debug, Default, Clone)]
pub struct Config {
@@ -428,7 +438,7 @@ pub struct TargetCfgs {
impl TargetCfgs {
fn new(config: &Config) -> TargetCfgs {
- let targets: HashMap<String, TargetCfg> = serde_json::from_str(&rustc_output(
+ let mut targets: HashMap<String, TargetCfg> = serde_json::from_str(&rustc_output(
config,
&["--print=all-target-specs-json", "-Zunstable-options"],
))
@@ -443,7 +453,19 @@ impl TargetCfgs {
let mut all_families = HashSet::new();
let mut all_pointer_widths = HashSet::new();
- for (target, cfg) in targets.into_iter() {
+ // Handle custom target specs, which are not included in `--print=all-target-specs-json`.
+ if config.target.ends_with(".json") {
+ targets.insert(
+ config.target.clone(),
+ serde_json::from_str(&rustc_output(
+ config,
+ &["--print=target-spec-json", "-Zunstable-options", "--target", &config.target],
+ ))
+ .unwrap(),
+ );
+ }
+
+ for (target, cfg) in targets.iter() {
all_archs.insert(cfg.arch.clone());
all_oses.insert(cfg.os.clone());
all_oses_and_envs.insert(cfg.os_and_env());
@@ -454,11 +476,11 @@ impl TargetCfgs {
}
all_pointer_widths.insert(format!("{}bit", cfg.pointer_width));
- all_targets.insert(target.into());
+ all_targets.insert(target.clone());
}
Self {
- current: Self::get_current_target_config(config),
+ current: Self::get_current_target_config(config, &targets),
all_targets,
all_archs,
all_oses,
@@ -470,16 +492,20 @@ impl TargetCfgs {
}
}
- fn get_current_target_config(config: &Config) -> TargetCfg {
- let mut arch = None;
- let mut os = None;
- let mut env = None;
- let mut abi = None;
- let mut families = Vec::new();
- let mut pointer_width = None;
- let mut endian = None;
- let mut panic = None;
-
+ fn get_current_target_config(
+ config: &Config,
+ targets: &HashMap<String, TargetCfg>,
+ ) -> TargetCfg {
+ let mut cfg = targets[&config.target].clone();
+
+ // To get the target information for the current target, we take the target spec obtained
+ // from `--print=all-target-specs-json`, and then we enrich it with the information
+ // gathered from `--print=cfg --target=$target`.
+ //
+ // This is done because some parts of the target spec can be overridden with `-C` flags,
+ // which are respected for `--print=cfg` but not for `--print=all-target-specs-json`. The
+ // code below extracts them from `--print=cfg`: make sure to only override fields that can
+ // actually be changed with `-C` flags.
for config in
rustc_output(config, &["--print=cfg", "--target", &config.target]).trim().lines()
{
@@ -497,60 +523,16 @@ impl TargetCfgs {
})
.unwrap_or_else(|| (config, None));
- match name {
- "target_arch" => {
- arch = Some(value.expect("target_arch should be a key-value pair").to_string());
- }
- "target_os" => {
- os = Some(value.expect("target_os sould be a key-value pair").to_string());
- }
- "target_env" => {
- env = Some(value.expect("target_env should be a key-value pair").to_string());
- }
- "target_abi" => {
- abi = Some(value.expect("target_abi should be a key-value pair").to_string());
- }
- "target_family" => {
- families
- .push(value.expect("target_family should be a key-value pair").to_string());
- }
- "target_pointer_width" => {
- pointer_width = Some(
- value
- .expect("target_pointer_width should be a key-value pair")
- .parse::<u32>()
- .expect("target_pointer_width should be a valid u32"),
- );
- }
- "target_endian" => {
- endian = Some(match value.expect("target_endian should be a key-value pair") {
- "big" => Endian::Big,
- "little" => Endian::Little,
- _ => panic!("target_endian should be either 'big' or 'little'"),
- });
- }
- "panic" => {
- panic = Some(match value.expect("panic should be a key-value pair") {
- "abort" => PanicStrategy::Abort,
- "unwind" => PanicStrategy::Unwind,
- _ => panic!("panic should be either 'abort' or 'unwind'"),
- });
- }
- _ => (),
+ match (name, value) {
+ // Can be overridden with `-C panic=$strategy`.
+ ("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
+ ("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
+ ("panic", other) => panic!("unexpected value for panic cfg: {other:?}"),
+ _ => {}
}
}
- TargetCfg {
- arch: arch.expect("target configuration should specify target_arch"),
- os: os.expect("target configuration should specify target_os"),
- env: env.expect("target configuration should specify target_env"),
- abi: abi.expect("target configuration should specify target_abi"),
- families,
- pointer_width: pointer_width
- .expect("target configuration should specify target_pointer_width"),
- endian: endian.expect("target configuration should specify target_endian"),
- panic: panic.expect("target configuration should specify panic"),
- }
+ cfg
}
}
@@ -571,7 +553,9 @@ pub struct TargetCfg {
#[serde(rename = "target-endian", default)]
endian: Endian,
#[serde(rename = "panic-strategy", default)]
- panic: PanicStrategy,
+ pub(crate) panic: PanicStrategy,
+ #[serde(default)]
+ pub(crate) dynamic_linking: bool,
}
impl TargetCfg {
@@ -654,6 +638,7 @@ pub const UI_EXTENSIONS: &[&str] = &[
UI_STDERR_64,
UI_STDERR_32,
UI_STDERR_16,
+ UI_COVERAGE,
];
pub const UI_STDERR: &str = "stderr";
pub const UI_STDOUT: &str = "stdout";
@@ -663,6 +648,7 @@ pub const UI_RUN_STDOUT: &str = "run.stdout";
pub const UI_STDERR_64: &str = "64bit.stderr";
pub const UI_STDERR_32: &str = "32bit.stderr";
pub const UI_STDERR_16: &str = "16bit.stderr";
+pub const UI_COVERAGE: &str = "coverage";
/// Absolute path to the directory where all output for all tests in the given
/// `relative_dir` group should reside. Example:
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 8cc935e54..c835962ad 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -161,7 +161,7 @@ pub struct TestProps {
// customized normalization rules
pub normalize_stdout: Vec<(String, String)>,
pub normalize_stderr: Vec<(String, String)>,
- pub failure_status: i32,
+ pub failure_status: Option<i32>,
// For UI tests, allows compiler to exit with arbitrary failure status
pub dont_check_failure_status: bool,
// Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the
@@ -257,7 +257,7 @@ impl TestProps {
check_test_line_numbers_match: false,
normalize_stdout: vec![],
normalize_stderr: vec![],
- failure_status: -1,
+ failure_status: None,
dont_check_failure_status: false,
run_rustfix: false,
rustfix_only_machine_applicable: false,
@@ -428,7 +428,7 @@ impl TestProps {
.parse_name_value_directive(ln, FAILURE_STATUS)
.and_then(|code| code.trim().parse::<i32>().ok())
{
- self.failure_status = code;
+ self.failure_status = Some(code);
}
config.set_name_directive(
@@ -491,11 +491,8 @@ impl TestProps {
});
}
- if self.failure_status == -1 {
- self.failure_status = 1;
- }
if self.should_ice {
- self.failure_status = 101;
+ self.failure_status = Some(101);
}
if config.mode == Mode::Incremental {
@@ -615,10 +612,25 @@ pub fn line_directive<'line>(
}
fn iter_header<R: Read>(testfile: &Path, rdr: R, it: &mut dyn FnMut(Option<&str>, &str, usize)) {
+ iter_header_extra(testfile, rdr, &[], it)
+}
+
+fn iter_header_extra(
+ testfile: &Path,
+ rdr: impl Read,
+ extra_directives: &[&str],
+ it: &mut dyn FnMut(Option<&str>, &str, usize),
+) {
if testfile.is_dir() {
return;
}
+ // Process any extra directives supplied by the caller (e.g. because they
+ // are implied by the test mode), with a dummy line number of 0.
+ for directive in extra_directives {
+ it(None, directive, 0);
+ }
+
let comment = if testfile.extension().map(|e| e == "rs") == Some(true) { "//" } else { "#" };
let mut rdr = BufReader::new(rdr);
@@ -897,7 +909,27 @@ pub fn make_test_description<R: Read>(
let mut ignore_message = None;
let mut should_fail = false;
- iter_header(path, src, &mut |revision, ln, line_number| {
+ let extra_directives: &[&str] = match config.mode {
+ // The run-coverage 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 => {
+ &[
+ "needs-profiler-support",
+ // FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
+ // properly. Since we only have GCC on the CI ignore the test for now.
+ "ignore-windows-gnu",
+ // FIXME(pietroalbini): this test currently does not work on cross-compiled
+ // targets because remote-test is not capable of sending back the *.profraw
+ // files generated by the LLVM instrumentation.
+ "ignore-cross-compile",
+ ]
+ }
+ _ => &[],
+ };
+
+ iter_header_extra(path, src, extra_directives, &mut |revision, ln, line_number| {
if revision.is_some() && revision != cfg {
return;
}
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index 4a57c6140..62364ede4 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -71,6 +71,11 @@ pub(super) fn handle_needs(
ignore_reason: "ignored on targets without shadow call stacks",
},
Need {
+ name: "needs-sanitizer-safestack",
+ condition: cache.sanitizer_safestack,
+ ignore_reason: "ignored on targets without SafeStack support",
+ },
+ Need {
name: "needs-run-enabled",
condition: config.run_enabled(),
ignore_reason: "ignored when running the resulting test binaries is disabled",
@@ -82,7 +87,7 @@ pub(super) fn handle_needs(
},
Need {
name: "needs-profiler-support",
- condition: std::env::var_os("RUSTC_PROFILER_SUPPORT").is_some(),
+ condition: cache.profiler_support,
ignore_reason: "ignored when profiler support is disabled",
},
Need {
@@ -125,6 +130,11 @@ pub(super) fn handle_needs(
condition: config.git_hash,
ignore_reason: "ignored when git hashes have been omitted for building",
},
+ Need {
+ name: "needs-dynamic-linking",
+ condition: config.target_cfg().dynamic_linking,
+ ignore_reason: "ignored on targets without dynamic linking",
+ },
];
let (name, comment) = match ln.split_once([':', ' ']) {
@@ -184,6 +194,8 @@ pub(super) struct CachedNeedsConditions {
sanitizer_hwaddress: bool,
sanitizer_memtag: bool,
sanitizer_shadow_call_stack: bool,
+ sanitizer_safestack: bool,
+ profiler_support: bool,
xray: bool,
rust_lld: bool,
i686_dlltool: bool,
@@ -220,6 +232,8 @@ impl CachedNeedsConditions {
sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target),
sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target),
+ sanitizer_safestack: util::SAFESTACK_SUPPORTED_TARGETS.contains(target),
+ profiler_support: std::env::var_os("RUSTC_PROFILER_SUPPORT").is_some(),
xray: util::XRAY_SUPPORTED_TARGETS.contains(target),
// For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs
index 725f7a151..a455a1bad 100644
--- a/src/tools/compiletest/src/read2.rs
+++ b/src/tools/compiletest/src/read2.rs
@@ -83,7 +83,7 @@ impl ProcOutput {
}
let new_len = bytes.len();
- if *filtered_len <= HEAD_LEN + TAIL_LEN {
+ if (*filtered_len).min(new_len) <= HEAD_LEN + TAIL_LEN {
return;
}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 5bc4d1642..672779325 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -6,8 +6,8 @@ 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::{Pretty, RunPassValgrind};
-use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
+use crate::common::{Pretty, RunCoverage, RunPassValgrind};
+use crate::common::{UI_COVERAGE, UI_RUN_STDERR, UI_RUN_STDOUT};
use crate::compute_diff::{write_diff, write_filtered_diff};
use crate::errors::{self, Error, ErrorKind};
use crate::header::TestProps;
@@ -253,6 +253,7 @@ impl<'test> TestCx<'test> {
MirOpt => self.run_mir_opt_test(),
Assembly => self.run_assembly_test(),
JsDocTest => self.run_js_doc_test(),
+ RunCoverage => self.run_coverage_test(),
}
}
@@ -384,7 +385,7 @@ impl<'test> TestCx<'test> {
}
fn check_correct_failure_status(&self, proc_res: &ProcRes) {
- let expected_status = Some(self.props.failure_status);
+ let expected_status = Some(self.props.failure_status.unwrap_or(1));
let received_status = proc_res.status.code();
if expected_status != received_status {
@@ -465,6 +466,296 @@ impl<'test> TestCx<'test> {
}
}
+ fn run_coverage_test(&self) {
+ let should_run = self.run_if_enabled();
+ let proc_res = self.compile_test(should_run, Emit::None);
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+ drop(proc_res);
+
+ if let WillExecute::Disabled = should_run {
+ return;
+ }
+
+ let profraw_path = self.output_base_dir().join("default.profraw");
+ let profdata_path = self.output_base_dir().join("default.profdata");
+
+ // Delete any existing profraw/profdata files to rule out unintended
+ // interference between repeated test runs.
+ if profraw_path.exists() {
+ std::fs::remove_file(&profraw_path).unwrap();
+ }
+ if profdata_path.exists() {
+ std::fs::remove_file(&profdata_path).unwrap();
+ }
+
+ let proc_res = self.exec_compiled_test_general(
+ &[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())],
+ false,
+ );
+ if self.props.failure_status.is_some() {
+ self.check_correct_failure_status(&proc_res);
+ } else if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ drop(proc_res);
+
+ let mut profraw_paths = vec![profraw_path];
+ let mut bin_paths = vec![self.make_exe_name()];
+
+ if self.config.suite == "run-coverage-rustdoc" {
+ self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths);
+ }
+
+ // Run `llvm-profdata merge` to index the raw coverage output.
+ let proc_res = self.run_llvm_tool("llvm-profdata", |cmd| {
+ cmd.args(["merge", "--sparse", "--output"]);
+ cmd.arg(&profdata_path);
+ cmd.args(&profraw_paths);
+ });
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("llvm-profdata merge failed!", &proc_res);
+ }
+ drop(proc_res);
+
+ // Run `llvm-cov show` to produce a coverage report in text format.
+ let proc_res = self.run_llvm_tool("llvm-cov", |cmd| {
+ cmd.args(["show", "--format=text", "--show-line-counts-or-regions"]);
+
+ cmd.arg("--Xdemangler");
+ cmd.arg(self.config.rust_demangler_path.as_ref().unwrap());
+
+ cmd.arg("--instr-profile");
+ cmd.arg(&profdata_path);
+
+ for bin in &bin_paths {
+ cmd.arg("--object");
+ cmd.arg(bin);
+ }
+ });
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("llvm-cov show failed!", &proc_res);
+ }
+
+ let kind = UI_COVERAGE;
+
+ let expected_coverage = self.load_expected_output(kind);
+ let normalized_actual_coverage =
+ self.normalize_coverage_output(&proc_res.stdout).unwrap_or_else(|err| {
+ self.fatal_proc_rec(&err, &proc_res);
+ });
+
+ let coverage_errors = self.compare_output(
+ kind,
+ &normalized_actual_coverage,
+ &expected_coverage,
+ self.props.compare_output_lines_by_subset,
+ );
+
+ if coverage_errors > 0 {
+ self.fatal_proc_rec(
+ &format!("{} errors occurred comparing coverage output.", coverage_errors),
+ &proc_res,
+ );
+ }
+ }
+
+ /// Run any doctests embedded in this test file, and add any resulting
+ /// `.profraw` files and doctest executables to the given vectors.
+ fn run_doctests_for_coverage(
+ &self,
+ profraw_paths: &mut Vec<PathBuf>,
+ bin_paths: &mut Vec<PathBuf>,
+ ) {
+ // Put .profraw files and doctest executables in dedicated directories,
+ // to make it easier to glob them all later.
+ let profraws_dir = self.output_base_dir().join("doc_profraws");
+ let bins_dir = self.output_base_dir().join("doc_bins");
+
+ // Remove existing directories to prevent cross-run interference.
+ if profraws_dir.try_exists().unwrap() {
+ std::fs::remove_dir_all(&profraws_dir).unwrap();
+ }
+ if bins_dir.try_exists().unwrap() {
+ std::fs::remove_dir_all(&bins_dir).unwrap();
+ }
+
+ let mut rustdoc_cmd =
+ Command::new(self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed"));
+
+ // In general there will be multiple doctest binaries running, so we
+ // tell the profiler runtime to write their coverage data into separate
+ // profraw files.
+ rustdoc_cmd.env("LLVM_PROFILE_FILE", profraws_dir.join("%p-%m.profraw"));
+
+ rustdoc_cmd.args(["--test", "-Cinstrument-coverage"]);
+
+ // Without this, the doctests complain about not being able to find
+ // their enclosing file's crate for some reason.
+ rustdoc_cmd.args(["--crate-name", "workaround_for_79771"]);
+
+ // Persist the doctest binaries so that `llvm-cov show` can read their
+ // embedded coverage mappings later.
+ rustdoc_cmd.arg("-Zunstable-options");
+ rustdoc_cmd.arg("--persist-doctests");
+ rustdoc_cmd.arg(&bins_dir);
+
+ rustdoc_cmd.arg("-L");
+ rustdoc_cmd.arg(self.aux_output_dir_name());
+
+ rustdoc_cmd.arg(&self.testpaths.file);
+
+ let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("rustdoc --test failed!", &proc_res)
+ }
+
+ fn glob_iter(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
+ let path_str = path.as_ref().to_str().unwrap();
+ let iter = glob(path_str).unwrap();
+ iter.map(Result::unwrap)
+ }
+
+ // Find all profraw files in the profraw directory.
+ for p in glob_iter(profraws_dir.join("*.profraw")) {
+ profraw_paths.push(p);
+ }
+ // Find all executables in the `--persist-doctests` directory, while
+ // avoiding other file types (e.g. `.pdb` on Windows). This doesn't
+ // need to be perfect, as long as it can handle the files actually
+ // produced by `rustdoc --test`.
+ for p in glob_iter(bins_dir.join("**/*")) {
+ let is_bin = p.is_file()
+ && match p.extension() {
+ None => true,
+ Some(ext) => ext == OsStr::new("exe"),
+ };
+ if is_bin {
+ bin_paths.push(p);
+ }
+ }
+ }
+
+ fn run_llvm_tool(&self, name: &str, configure_cmd_fn: impl FnOnce(&mut Command)) -> ProcRes {
+ let tool_path = self
+ .config
+ .llvm_bin_dir
+ .as_ref()
+ .expect("this test expects the LLVM bin dir to be available")
+ .join(name);
+
+ let mut cmd = Command::new(tool_path);
+ configure_cmd_fn(&mut cmd);
+
+ let output = cmd.output().unwrap_or_else(|_| panic!("failed to exec `{cmd:?}`"));
+
+ let proc_res = ProcRes {
+ status: output.status,
+ stdout: String::from_utf8(output.stdout).unwrap(),
+ stderr: String::from_utf8(output.stderr).unwrap(),
+ cmdline: format!("{cmd:?}"),
+ };
+ self.dump_output(&proc_res.stdout, &proc_res.stderr);
+
+ proc_res
+ }
+
+ fn normalize_coverage_output(&self, coverage: &str) -> Result<String, String> {
+ let normalized = self.normalize_output(coverage, &[]);
+
+ let mut lines = normalized.lines().collect::<Vec<_>>();
+
+ Self::sort_coverage_file_sections(&mut lines)?;
+ Self::sort_coverage_subviews(&mut lines)?;
+
+ let joined_lines = lines.iter().flat_map(|line| [line, "\n"]).collect::<String>();
+ Ok(joined_lines)
+ }
+
+ /// Coverage reports can describe multiple source files, separated by
+ /// blank lines. The order of these files is unpredictable (since it
+ /// depends on implementation details), so we need to sort the file
+ /// sections into a consistent order before comparing against a snapshot.
+ fn sort_coverage_file_sections(coverage_lines: &mut Vec<&str>) -> Result<(), String> {
+ // Group the lines into file sections, separated by blank lines.
+ let mut sections = coverage_lines.split(|line| line.is_empty()).collect::<Vec<_>>();
+
+ // The last section should be empty, representing an extra trailing blank line.
+ if !sections.last().is_some_and(|last| last.is_empty()) {
+ return Err("coverage report should end with an extra blank line".to_owned());
+ }
+
+ // Sort the file sections (not including the final empty "section").
+ let except_last = sections.len() - 1;
+ (&mut sections[..except_last]).sort();
+
+ // Join the file sections back into a flat list of lines, with
+ // sections separated by blank lines.
+ let joined = sections.join(&[""] as &[_]);
+ assert_eq!(joined.len(), coverage_lines.len());
+ *coverage_lines = joined;
+
+ Ok(())
+ }
+
+ fn sort_coverage_subviews(coverage_lines: &mut Vec<&str>) -> Result<(), String> {
+ let mut output_lines = Vec::new();
+
+ // We accumulate a list of zero or more "subviews", where each
+ // subview is a list of one or more lines.
+ let mut subviews: Vec<Vec<&str>> = Vec::new();
+
+ fn flush<'a>(subviews: &mut Vec<Vec<&'a str>>, output_lines: &mut Vec<&'a str>) {
+ if subviews.is_empty() {
+ return;
+ }
+
+ // Take and clear the list of accumulated subviews.
+ let mut subviews = std::mem::take(subviews);
+
+ // The last "subview" should be just a boundary line on its own,
+ // so exclude it when sorting the other subviews.
+ let except_last = subviews.len() - 1;
+ (&mut subviews[..except_last]).sort();
+
+ for view in subviews {
+ for line in view {
+ output_lines.push(line);
+ }
+ }
+ }
+
+ for (line, line_num) in coverage_lines.iter().zip(1..) {
+ if line.starts_with(" ------------------") {
+ // This is a subview boundary line, so start a new subview.
+ subviews.push(vec![line]);
+ } else if line.starts_with(" |") {
+ // Add this line to the current subview.
+ subviews
+ .last_mut()
+ .ok_or(format!(
+ "unexpected subview line outside of a subview on line {line_num}"
+ ))?
+ .push(line);
+ } else {
+ // This line is not part of a subview, so sort and print any
+ // accumulated subviews, and then print the line as-is.
+ flush(&mut subviews, &mut output_lines);
+ output_lines.push(line);
+ }
+ }
+
+ flush(&mut subviews, &mut output_lines);
+ assert!(subviews.is_empty());
+
+ assert_eq!(output_lines.len(), coverage_lines.len());
+ *coverage_lines = output_lines;
+
+ Ok(())
+ }
+
fn run_pretty_test(&self) {
if self.props.pp_exact.is_some() {
logv(self.config, "testing for exact pretty-printing".to_owned());
@@ -1598,7 +1889,26 @@ impl<'test> TestCx<'test> {
}
fn exec_compiled_test(&self) -> ProcRes {
- let env = &self.props.exec_env;
+ self.exec_compiled_test_general(&[], true)
+ }
+
+ fn exec_compiled_test_general(
+ &self,
+ env_extra: &[(&str, &str)],
+ delete_after_success: bool,
+ ) -> ProcRes {
+ let prepare_env = |cmd: &mut Command| {
+ for key in &self.props.unset_exec_env {
+ cmd.env_remove(key);
+ }
+
+ for (key, val) in &self.props.exec_env {
+ cmd.env(key, val);
+ }
+ for (key, val) in env_extra {
+ cmd.env(key, val);
+ }
+ };
let proc_res = match &*self.config.target {
// This is pretty similar to below, we're transforming:
@@ -1635,10 +1945,7 @@ impl<'test> TestCx<'test> {
.args(support_libs)
.args(args);
- for key in &self.props.unset_exec_env {
- test_client.env_remove(key);
- }
- test_client.envs(env.clone());
+ prepare_env(&mut test_client);
self.compose_and_run(
test_client,
@@ -1653,10 +1960,7 @@ impl<'test> TestCx<'test> {
let mut wr_run = Command::new("wr-run");
wr_run.args(&[&prog]).args(args);
- for key in &self.props.unset_exec_env {
- wr_run.env_remove(key);
- }
- wr_run.envs(env.clone());
+ prepare_env(&mut wr_run);
self.compose_and_run(
wr_run,
@@ -1671,10 +1975,7 @@ impl<'test> TestCx<'test> {
let mut program = Command::new(&prog);
program.args(args).current_dir(&self.output_base_dir());
- for key in &self.props.unset_exec_env {
- program.env_remove(key);
- }
- program.envs(env.clone());
+ prepare_env(&mut program);
self.compose_and_run(
program,
@@ -1685,7 +1986,7 @@ impl<'test> TestCx<'test> {
}
};
- if proc_res.status.success() {
+ if delete_after_success && proc_res.status.success() {
// delete the executable after running it to save space.
// it is ok if the deletion failed.
let _ = fs::remove_file(self.make_exe_name());
@@ -1810,8 +2111,9 @@ impl<'test> TestCx<'test> {
|| self.config.target.contains("wasm32")
|| self.config.target.contains("nvptx")
|| self.is_vxworks_pure_static()
- || self.config.target.contains("sgx")
|| self.config.target.contains("bpf")
+ || !self.config.target_cfg().dynamic_linking
+ || self.config.mode == RunCoverage
{
// We primarily compile all auxiliary libraries as dynamic libraries
// to avoid code size bloat and large binaries as much as possible
@@ -1822,6 +2124,10 @@ impl<'test> TestCx<'test> {
// dynamic libraries so we just go back to building a normal library. Note,
// however, that for MUSL if the library is built with `force_host` then
// it's ok to be a dylib as the host should always support dylibs.
+ //
+ // Coverage tests want static linking by default so that coverage
+ // mappings in auxiliary libraries can be merged into the final
+ // executable.
(false, Some("lib"))
} else {
(true, Some("dylib"))
@@ -1939,8 +2245,21 @@ impl<'test> TestCx<'test> {
// Use a single thread for efficiency and a deterministic error message order
rustc.arg("-Zthreads=1");
+ // Hide libstd sources from ui tests to make sure we generate the stderr
+ // output that users will see.
+ // Without this, we may be producing good diagnostics in-tree but users
+ // will not see half the information.
+ //
+ // This also has the benefit of more effectively normalizing output between different
+ // compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the
+ // fact.
+ rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
+ rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
+
// Optionally prevent default --sysroot if specified in test compile-flags.
- if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot")) {
+ if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
+ && !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
+ {
// In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
rustc.arg("--sysroot").arg(&self.config.sysroot_base);
}
@@ -1986,6 +2305,10 @@ impl<'test> TestCx<'test> {
}
}
DebugInfo => { /* debuginfo tests must be unoptimized */ }
+ RunCoverage => {
+ // Coverage reports are affected by optimization level, and
+ // the current snapshots assume no optimization by default.
+ }
_ => {
rustc.arg("-O");
}
@@ -2014,13 +2337,6 @@ impl<'test> TestCx<'test> {
rustc.arg("-Ccodegen-units=1");
// Hide line numbers to reduce churn
rustc.arg("-Zui-testing");
- // Hide libstd sources from ui tests to make sure we generate the stderr
- // output that users will see.
- // Without this, we may be producing good diagnostics in-tree but users
- // will not see half the information.
- rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
- rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
-
rustc.arg("-Zdeduplicate-diagnostics=no");
// FIXME: use this for other modes too, for perf?
rustc.arg("-Cstrip=debuginfo");
@@ -2040,12 +2356,14 @@ impl<'test> TestCx<'test> {
&zdump_arg,
"-Zvalidate-mir",
"-Zdump-mir-exclude-pass-number",
- "-Zmir-pretty-relative-line-numbers=yes",
]);
if let Some(pass) = &self.props.mir_unit_test {
rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
} else {
- rustc.arg("-Zmir-opt-level=4");
+ rustc.args(&[
+ "-Zmir-opt-level=4",
+ "-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals",
+ ]);
}
let mir_dump_dir = self.get_mir_dump_dir();
@@ -2057,6 +2375,9 @@ impl<'test> TestCx<'test> {
rustc.arg(dir_opt);
}
+ RunCoverage => {
+ rustc.arg("-Cinstrument-coverage");
+ }
RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson | RunMake
| CodegenUnits | JsDocTest | Assembly => {
// do not use JSON output
@@ -2114,12 +2435,12 @@ impl<'test> TestCx<'test> {
Some(CompareMode::Polonius) => {
rustc.args(&["-Zpolonius"]);
}
- Some(CompareMode::Chalk) => {
- rustc.args(&["-Ztrait-solver=chalk"]);
- }
Some(CompareMode::NextSolver) => {
rustc.args(&["-Ztrait-solver=next"]);
}
+ Some(CompareMode::NextSolverCoherence) => {
+ rustc.args(&["-Ztrait-solver=next-coherence"]);
+ }
Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
}
@@ -3555,6 +3876,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(),
);
let mut out = Vec::new();
@@ -3572,25 +3894,24 @@ impl<'test> TestCx<'test> {
}
fn check_mir_dump(&self) {
- let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
-
let test_dir = self.testpaths.file.parent().unwrap();
let test_crate =
self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_");
- let mut bit_width = String::new();
- if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") {
- bit_width = format!(".{}bit", self.config.get_pointer_width());
- }
+ 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(),
+ );
if self.config.bless {
for e in
- glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, bit_width)).unwrap()
+ glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap()
{
std::fs::remove_file(e.unwrap()).unwrap();
}
for e in
- glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, bit_width)).unwrap()
+ glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap()
{
std::fs::remove_file(e.unwrap()).unwrap();
}
@@ -3599,6 +3920,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
@@ -3700,8 +4022,11 @@ impl<'test> TestCx<'test> {
}
fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
+ let rflags = self.props.run_flags.as_ref();
let cflags = self.props.compile_flags.join(" ");
- let json = cflags.contains("--error-format json")
+ let json = rflags
+ .map_or(false, |s| s.contains("--format json") || s.contains("--format=json"))
+ || cflags.contains("--error-format json")
|| cflags.contains("--error-format pretty-json")
|| cflags.contains("--error-format=json")
|| cflags.contains("--error-format=pretty-json")
@@ -3729,28 +4054,13 @@ impl<'test> TestCx<'test> {
normalize_path(&remapped_parent_dir, "$DIR");
}
- let source_bases = &[
- // Source base on the current filesystem (calculated as parent of `tests/$suite`):
- Some(self.config.src_base.parent().unwrap().parent().unwrap().into()),
- // Source base on the sysroot (from the src components downloaded by `download-rustc`):
- Some(self.config.sysroot_base.join("lib").join("rustlib").join("src").join("rust")),
- // Virtual `/rustc/$sha` remapped paths (if `remap-debuginfo` is enabled):
- option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from),
- // Virtual `/rustc/$sha` coming from download-rustc:
- std::env::var_os("FAKE_DOWNLOAD_RUSTC_PREFIX").map(PathBuf::from),
- // Tests using -Zsimulate-remapped-rust-src-base should use this fake path
- Some("/rustc/FAKE_PREFIX".into()),
- ];
- for base_dir in source_bases {
- if let Some(base_dir) = base_dir {
- // Paths into the libstd/libcore
- normalize_path(&base_dir.join("library"), "$SRC_DIR");
- // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
- // `rustc_macros`
- // eg. /home/user/rust/compiler
- normalize_path(&base_dir.join("compiler"), "$COMPILER_DIR");
- }
- }
+ let base_dir = Path::new("/rustc/FAKE_PREFIX");
+ // Paths into the libstd/libcore
+ normalize_path(&base_dir.join("library"), "$SRC_DIR");
+ // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
+ // `rustc_macros`
+ // eg. /home/user/rust/compiler
+ normalize_path(&base_dir.join("compiler"), "$COMPILER_DIR");
// Paths into the build directory
let test_build_dir = &self.config.build_base;
@@ -4064,7 +4374,7 @@ impl ProcRes {
pub fn print_info(&self) {
fn render(name: &str, contents: &str) -> String {
let contents = json::extract_rendered(contents);
- let contents = contents.trim();
+ let contents = contents.trim_end();
if contents.is_empty() {
format!("{name}: none")
} else {
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 748240cc9..17bed38b6 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -104,6 +104,8 @@ pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[
"x86_64-unknown-openbsd",
];
+pub const SAFESTACK_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
+
pub fn make_new_path(path: &str) -> String {
assert!(cfg!(windows));
// Windows just uses PATH as the library search path, so we have to
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index f984275b1..62a58576d 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -1,6 +1,7 @@
#![feature(rustc_private)]
extern crate rustc_driver;
+extern crate rustc_session;
use std::env;
use std::error::Error;
@@ -170,7 +171,9 @@ fn parse_args() -> (OutputFormat, PathBuf) {
}
fn main() {
- rustc_driver::init_env_logger("RUST_LOG");
+ let handler =
+ rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
+ rustc_driver::init_env_logger(&handler, "RUST_LOG");
let (format, dst) = parse_args();
let result = main_with_result(format, &dst);
if let Err(e) = result {
diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs
index f86c3ce0a..e33ecfe8e 100644
--- a/src/tools/miropt-test-tools/src/lib.rs
+++ b/src/tools/miropt-test-tools/src/lib.rs
@@ -1,4 +1,5 @@
use std::fs;
+use std::path::Path;
pub struct MiroptTestFiles {
pub expected_file: std::path::PathBuf,
@@ -8,18 +9,52 @@ pub struct MiroptTestFiles {
pub passes: Vec<String>,
}
-pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<MiroptTestFiles> {
+pub enum PanicStrategy {
+ Unwind,
+ Abort,
+}
+
+pub 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() {
+ if line == "// EMIT_MIR_FOR_EACH_BIT_WIDTH" {
+ each_bit_width = true;
+ }
+ if line == "// EMIT_MIR_FOR_EACH_PANIC_STRATEGY" {
+ each_panic_strategy = true;
+ }
+ }
+
+ let mut suffix = String::new();
+ if each_bit_width {
+ suffix.push_str(&format!(".{}bit", bit_width));
+ }
+ if each_panic_strategy {
+ match panic_strategy {
+ PanicStrategy::Unwind => suffix.push_str(".panic-unwind"),
+ PanicStrategy::Abort => suffix.push_str(".panic-abort"),
+ }
+ }
+ suffix
+}
+
+pub fn files_for_miropt_test(
+ testfile: &std::path::Path,
+ bit_width: u32,
+ panic_strategy: PanicStrategy,
+) -> Vec<MiroptTestFiles> {
let mut out = Vec::new();
let test_file_contents = fs::read_to_string(&testfile).unwrap();
let test_dir = testfile.parent().unwrap();
let test_crate = testfile.file_stem().unwrap().to_str().unwrap().replace('-', "_");
- let bit_width = if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") {
- format!(".{}bit", bit_width)
- } else {
- String::new()
- };
+ let suffix = output_file_suffix(testfile, bit_width, panic_strategy);
for l in test_file_contents.lines() {
if l.starts_with("// EMIT_MIR ") {
@@ -37,7 +72,7 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
passes.push(trimmed.split('.').last().unwrap().to_owned());
let test_against = format!("{}.after.mir", trimmed);
from_file = format!("{}.before.mir", trimmed);
- expected_file = format!("{}{}.diff", trimmed, bit_width);
+ expected_file = format!("{}{}.diff", trimmed, suffix);
assert!(test_names.next().is_none(), "two mir pass names specified for MIR diff");
to_file = Some(test_against);
} else if let Some(first_pass) = test_names.next() {
@@ -51,7 +86,7 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
assert!(test_names.next().is_none(), "three mir pass names specified for MIR diff");
expected_file =
- format!("{}{}.{}-{}.diff", test_name, bit_width, first_pass, second_pass);
+ format!("{}{}.{}-{}.diff", test_name, suffix, first_pass, second_pass);
let second_file = format!("{}.{}.mir", test_name, second_pass);
from_file = format!("{}.{}.mir", test_name, first_pass);
to_file = Some(second_file);
@@ -64,7 +99,7 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
let extension = cap.get(1).unwrap().as_str();
expected_file =
- format!("{}{}{}", test_name.trim_end_matches(extension), bit_width, extension,);
+ format!("{}{}{}", test_name.trim_end_matches(extension), suffix, extension,);
from_file = test_name.to_string();
assert!(test_names.next().is_none(), "two mir pass names specified for MIR dump");
to_file = None;
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index 2018c239b..f9421117e 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -22,7 +22,7 @@ except ImportError:
import urllib.request as urllib2
from urllib.error import HTTPError
try:
- import typing
+ import typing # noqa: F401 FIXME: py2
except ImportError:
pass
@@ -152,8 +152,8 @@ def update_latest(
latest = json.load(f, object_pairs_hook=collections.OrderedDict)
current_status = {
- os: read_current_status(current_commit, 'history/' + os + '.tsv')
- for os in ['windows', 'linux']
+ os_: read_current_status(current_commit, 'history/' + os_ + '.tsv')
+ for os_ in ['windows', 'linux']
}
slug = 'rust-lang/rust'
@@ -170,10 +170,10 @@ def update_latest(
changed = False
create_issue_for_status = None # set to the status that caused the issue
- for os, s in current_status.items():
- old = status[os]
+ for os_, s in current_status.items():
+ old = status[os_]
new = s.get(tool, old)
- status[os] = new
+ status[os_] = new
maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ()))
# comparing the strings, but they are ordered appropriately:
# "test-pass" > "test-fail" > "build-fail"
@@ -181,12 +181,12 @@ def update_latest(
# things got fixed or at least the status quo improved
changed = True
message += '🎉 {} on {}: {} → {} (cc {}).\n' \
- .format(tool, os, old, new, maintainers)
+ .format(tool, os_, old, new, maintainers)
elif new < old:
# tests or builds are failing and were not failing before
changed = True
title = '💔 {} on {}: {} → {}' \
- .format(tool, os, old, new)
+ .format(tool, os_, old, new)
message += '{} (cc {}).\n' \
.format(title, maintainers)
# See if we need to create an issue.
diff --git a/src/tools/rust-analyzer/.vscode/launch.json b/src/tools/rust-analyzer/.vscode/launch.json
index 1e21214ff..c353737a3 100644
--- a/src/tools/rust-analyzer/.vscode/launch.json
+++ b/src/tools/rust-analyzer/.vscode/launch.json
@@ -72,7 +72,7 @@
},
{
// Used for testing the extension with a local build of the LSP server (in `target/release`)
- // with all other extendions loaded.
+ // with all other extensions loaded.
"name": "Run With Extensions",
"type": "extensionHost",
"request": "launch",
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 25242c602..13cb25f7b 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -19,18 +19,18 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "always-assert"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11"
+checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127"
dependencies = [
"log",
]
[[package]]
name = "anyhow"
-version = "1.0.68"
+version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
+checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "anymap"
@@ -40,9 +40,9 @@ checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72"
[[package]]
name = "arbitrary"
-version = "1.2.2"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0224938f92e7aef515fac2ff2d18bd1115c1394ddf4a092e0c87e8be9499ee5"
+checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
[[package]]
name = "arrayvec"
@@ -87,12 +87,14 @@ name = "base-db"
version = "0.0.0"
dependencies = [
"cfg",
+ "la-arena",
"profile",
"rustc-hash",
"salsa",
"stdx",
"syntax",
"test-utils",
+ "triomphe",
"tt",
"vfs",
]
@@ -104,6 +106,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
+
+[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -111,9 +119,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "camino"
-version = "1.1.2"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055"
+checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2"
dependencies = [
"serde",
]
@@ -129,9 +137,9 @@ dependencies = [
[[package]]
name = "cargo_metadata"
-version = "0.15.2"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a"
+checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
dependencies = [
"camino",
"cargo-platform",
@@ -143,9 +151,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.78"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg"
@@ -169,32 +177,32 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chalk-derive"
-version = "0.89.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea176c50987dc4765961aa165001e8eb5a722a26308c5797a47303ea91686aab"
+checksum = "c59178fded594fe78c47b841520e5a4399d00fe15fffee19b945958a878cd02d"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.15",
"synstructure",
]
[[package]]
name = "chalk-ir"
-version = "0.89.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "473b480241695428c14e8f84f1c9a47ef232450a50faf3a4041e5c9dc11e0a3b"
+checksum = "8824be92876823b828d551bb792f79eb1f69c69d1948abf69fccbf84e448e57b"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"chalk-derive",
"lazy_static",
]
[[package]]
name = "chalk-recursive"
-version = "0.89.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6764b4fe67cac3a3758185084efbfbd39bf0352795824ba849ddd2b64cd4bb28"
+checksum = "1e110d1260809c238072d1c8ef84060e39983e8ea9d4c6f74b19b0ebbf8904dc"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -205,9 +213,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
-version = "0.89.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55a7e6160966eceb6e7dcc2f479a2af4c477aaf5bccbc640d82515995ab1a6cc"
+checksum = "12200b19abf4b0633095f7bd099f3ef609d314754b6adb358c68cc04d10589e5"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -221,9 +229,9 @@ dependencies = [
[[package]]
name = "command-group"
-version = "2.0.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "026c3922235f9f7d78f21251a026f3acdeb7cce3deba107fe09a4bfa63d850a2"
+checksum = "5080df6b0f0ecb76cab30808f00d937ba725cebe266a3da8cd89dff92f2a9916"
dependencies = [
"nix",
"winapi",
@@ -257,9 +265,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.5.6"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -267,9 +275,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@@ -278,22 +286,22 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.13"
+version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
+checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
- "memoffset 0.7.1",
+ "memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.14"
+version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
+checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
@@ -313,13 +321,13 @@ dependencies = [
[[package]]
name = "derive_arbitrary"
-version = "1.2.2"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf460bbff5f571bfc762da5102729f59f338be7db17a21fade44c5c4f5005350"
+checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -342,24 +350,24 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
[[package]]
name = "either"
-version = "1.8.0"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "ena"
-version = "0.14.0"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
+checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1"
dependencies = [
"log",
]
[[package]]
name = "expect-test"
-version = "1.4.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3"
+checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
dependencies = [
"dissimilar",
"once_cell",
@@ -400,7 +408,6 @@ dependencies = [
"cargo_metadata",
"command-group",
"crossbeam-channel",
- "jod-thread",
"paths",
"rustc-hash",
"serde",
@@ -420,12 +427,6 @@ dependencies = [
]
[[package]]
-name = "fs_extra"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
-
-[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -442,9 +443,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
[[package]]
name = "gimli"
-version = "0.27.0"
+version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "hashbrown"
@@ -497,6 +498,7 @@ dependencies = [
"smallvec",
"stdx",
"syntax",
+ "triomphe",
"tt",
]
@@ -507,7 +509,7 @@ dependencies = [
"anymap",
"arrayvec",
"base-db",
- "bitflags",
+ "bitflags 2.1.0",
"cfg",
"cov-mark",
"dashmap",
@@ -533,6 +535,7 @@ dependencies = [
"syntax",
"test-utils",
"tracing",
+ "triomphe",
"tt",
]
@@ -557,6 +560,7 @@ dependencies = [
"stdx",
"syntax",
"tracing",
+ "triomphe",
"tt",
]
@@ -566,7 +570,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
- "bitflags",
+ "bitflags 2.1.0",
"chalk-derive",
"chalk-ir",
"chalk-recursive",
@@ -582,6 +586,7 @@ dependencies = [
"itertools",
"la-arena",
"limit",
+ "nohash-hasher",
"once_cell",
"profile",
"project-model",
@@ -594,6 +599,7 @@ dependencies = [
"tracing",
"tracing-subscriber",
"tracing-tree",
+ "triomphe",
"typed-arena",
]
@@ -603,7 +609,7 @@ version = "0.0.20221221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adabaadad9aa7576f97af02241cdf5554d62fb3d51a84cb05d77ba28edd3013f"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"hkalbasi-rustc-ap-rustc_index",
"tracing",
]
@@ -644,6 +650,7 @@ dependencies = [
"ide-diagnostics",
"ide-ssr",
"itertools",
+ "nohash-hasher",
"oorandom",
"profile",
"pulldown-cmark",
@@ -655,6 +662,7 @@ dependencies = [
"text-edit",
"toolchain",
"tracing",
+ "triomphe",
"url",
]
@@ -710,7 +718,9 @@ dependencies = [
"indexmap",
"itertools",
"limit",
+ "line-index",
"memchr",
+ "nohash-hasher",
"once_cell",
"oorandom",
"parser",
@@ -723,6 +733,7 @@ dependencies = [
"test-utils",
"text-edit",
"tracing",
+ "triomphe",
"xshell",
]
@@ -755,11 +766,13 @@ dependencies = [
"hir",
"ide-db",
"itertools",
+ "nohash-hasher",
"parser",
"stdx",
"syntax",
"test-utils",
"text-edit",
+ "triomphe",
]
[[package]]
@@ -774,9 +787,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.9.2"
+version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
@@ -788,7 +801,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"inotify-sys",
"libc",
]
@@ -819,6 +832,7 @@ dependencies = [
"hashbrown",
"once_cell",
"rustc-hash",
+ "triomphe",
]
[[package]]
@@ -832,9 +846,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "jod-thread"
@@ -858,7 +872,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"libc",
]
@@ -874,9 +888,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.139"
+version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "libloading"
@@ -890,9 +904,9 @@ dependencies = [
[[package]]
name = "libmimalloc-sys"
-version = "0.1.30"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8c7cbf8b89019683667e347572e6d55a7df7ea36b0c4ce69961b0cde67b174"
+checksum = "43a558e3d911bc3c7bfc8c78bc580b404d6e51c1cefbf656e176a94b49b0df40"
dependencies = [
"cc",
"libc",
@@ -903,6 +917,14 @@ name = "limit"
version = "0.0.0"
[[package]]
+name = "line-index"
+version = "0.1.0-pre.1"
+dependencies = [
+ "nohash-hasher",
+ "text-size",
+]
+
+[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -938,7 +960,7 @@ version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
@@ -977,36 +999,27 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
-version = "0.5.8"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "memoffset"
-version = "0.7.1"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
dependencies = [
"autocfg",
]
[[package]]
name = "mimalloc"
-version = "0.1.34"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dcb174b18635f7561a0c6c9fc2ce57218ac7523cf72c50af80e2d79ab8f3ba1"
+checksum = "3d88dad3f985ec267a3fcb7a1726f5cb1a7e8cad8b646e70a84f967210df23da"
dependencies = [
"libmimalloc-sys",
]
@@ -1047,19 +1060,25 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"cfg-if",
"libc",
"static_assertions",
]
[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
name = "notify"
-version = "5.0.0"
+version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
+checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -1068,7 +1087,7 @@ dependencies = [
"libc",
"mio",
"walkdir",
- "winapi",
+ "windows-sys",
]
[[package]]
@@ -1093,18 +1112,18 @@ dependencies = [
[[package]]
name = "object"
-version = "0.30.2"
+version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.17.0"
+version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "oorandom"
@@ -1173,16 +1192,16 @@ dependencies = [
"drop_bomb",
"expect-test",
"limit",
- "rustc-ap-rustc_lexer",
+ "ra-ap-rustc_lexer",
"sourcegen",
"stdx",
]
[[package]]
name = "paste"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
+checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "paths"
@@ -1242,6 +1261,7 @@ dependencies = [
"snap",
"stdx",
"tracing",
+ "triomphe",
"tt",
]
@@ -1257,6 +1277,7 @@ dependencies = [
"paths",
"proc-macro-api",
"proc-macro-test",
+ "stdx",
"tt",
]
@@ -1264,6 +1285,7 @@ dependencies = [
name = "proc-macro-srv-cli"
version = "0.0.0"
dependencies = [
+ "proc-macro-api",
"proc-macro-srv",
]
@@ -1282,9 +1304,9 @@ version = "0.0.0"
[[package]]
name = "proc-macro2"
-version = "1.0.50"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
@@ -1312,6 +1334,7 @@ dependencies = [
"cargo_metadata",
"cfg",
"expect-test",
+ "itertools",
"la-arena",
"paths",
"profile",
@@ -1322,6 +1345,7 @@ dependencies = [
"stdx",
"toolchain",
"tracing",
+ "triomphe",
]
[[package]]
@@ -1350,7 +1374,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"memchr",
"unicase",
]
@@ -1366,18 +1390,28 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.23"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
+name = "ra-ap-rustc_lexer"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3"
+dependencies = [
+ "unic-emoji-char",
+ "unicode-xid",
+]
+
+[[package]]
name = "rayon"
-version = "1.6.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
@@ -1385,9 +1419,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.10.1"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -1401,14 +1435,14 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
name = "regex"
-version = "1.7.1"
+version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
+checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"regex-syntax",
]
@@ -1424,19 +1458,19 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.28"
+version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "rowan"
-version = "0.15.10"
+version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332"
+checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
dependencies = [
"countme",
"hashbrown",
- "memoffset 0.6.5",
+ "memoffset",
"rustc-hash",
"text-size",
]
@@ -1451,6 +1485,7 @@ dependencies = [
"crossbeam-channel",
"dissimilar",
"expect-test",
+ "filetime",
"flycheck",
"hir",
"hir-def",
@@ -1459,16 +1494,17 @@ dependencies = [
"ide-db",
"ide-ssr",
"itertools",
- "jod-thread",
"lsp-server",
"lsp-types",
"mbe",
"mimalloc",
+ "mio",
+ "nohash-hasher",
"num_cpus",
"oorandom",
"parking_lot 0.12.1",
+ "parking_lot_core 0.9.6",
"proc-macro-api",
- "proc-macro-srv",
"profile",
"project-model",
"rayon",
@@ -1476,17 +1512,19 @@ dependencies = [
"scip",
"serde",
"serde_json",
+ "serde_repr",
"sourcegen",
"stdx",
"syntax",
"test-utils",
- "threadpool",
+ "thiserror",
"tikv-jemallocator",
"toolchain",
"tracing",
"tracing-log",
"tracing-subscriber",
"tracing-tree",
+ "triomphe",
"tt",
"vfs",
"vfs-notify",
@@ -1496,19 +1534,10 @@ dependencies = [
]
[[package]]
-name = "rustc-ap-rustc_lexer"
-version = "727.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f40f26e7abdcd3b982f36c09a634cc6187988fbf6ec466c91f8d30a12ac0237"
-dependencies = [
- "unicode-xid",
-]
-
-[[package]]
name = "rustc-demangle"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b"
[[package]]
name = "rustc-hash"
@@ -1518,9 +1547,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
-version = "1.0.12"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "salsa"
@@ -1548,7 +1577,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -1583,38 +1612,38 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
-version = "1.0.16"
+version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
+checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
dependencies = [
"serde",
]
[[package]]
name = "serde"
-version = "1.0.152"
+version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.152"
+version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "serde_json"
-version = "1.0.91"
+version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"indexmap",
"itoa",
@@ -1624,13 +1653,13 @@ dependencies = [
[[package]]
name = "serde_repr"
-version = "0.1.10"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e"
+checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -1650,9 +1679,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "smol_str"
-version = "0.1.23"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44"
+checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
dependencies = [
"serde",
]
@@ -1682,6 +1711,8 @@ version = "0.0.0"
dependencies = [
"always-assert",
"backtrace",
+ "crossbeam-channel",
+ "jod-thread",
"libc",
"miow",
"winapi",
@@ -1689,9 +1720,20 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.107"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
@@ -1700,13 +1742,13 @@ dependencies = [
[[package]]
name = "synstructure"
-version = "0.12.6"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
+checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.15",
"unicode-xid",
]
@@ -1724,15 +1766,16 @@ dependencies = [
"proc-macro2",
"profile",
"quote",
+ "ra-ap-rustc_lexer",
"rayon",
"rowan",
- "rustc-ap-rustc_lexer",
"rustc-hash",
"smol_str",
"sourcegen",
"stdx",
"test-utils",
"text-edit",
+ "triomphe",
"ungrammar",
]
@@ -1763,43 +1806,35 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
[[package]]
name = "thiserror"
-version = "1.0.38"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
+checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.38"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
+checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "thread_local"
-version = "1.1.4"
+version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
+ "cfg-if",
"once_cell",
]
[[package]]
-name = "threadpool"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
-dependencies = [
- "num_cpus",
-]
-
-[[package]]
name = "tikv-jemalloc-ctl"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1812,12 +1847,11 @@ dependencies = [
[[package]]
name = "tikv-jemalloc-sys"
-version = "0.5.2+5.3.0-patched"
+version = "0.5.3+5.3.0-patched"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3"
+checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8"
dependencies = [
"cc",
- "fs_extra",
"libc",
]
@@ -1833,9 +1867,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.17"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
+checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"serde",
"time-core",
@@ -1858,9 +1892,9 @@ dependencies = [
[[package]]
name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toolchain"
@@ -1889,7 +1923,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -1943,6 +1977,12 @@ dependencies = [
]
[[package]]
+name = "triomphe"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db"
+
+[[package]]
name = "tt"
version = "0.0.0"
dependencies = [
@@ -1963,6 +2003,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"
[[package]]
+name = "unic-char-property"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
+dependencies = [
+ "unic-char-range",
+]
+
+[[package]]
+name = "unic-char-range"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
+
+[[package]]
+name = "unic-common"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
+
+[[package]]
+name = "unic-emoji-char"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d"
+dependencies = [
+ "unic-char-property",
+ "unic-char-range",
+ "unic-ucd-version",
+]
+
+[[package]]
+name = "unic-ucd-version"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
+dependencies = [
+ "unic-common",
+]
+
+[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1973,15 +2054,15 @@ dependencies = [
[[package]]
name = "unicode-bidi"
-version = "0.3.10"
+version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"
@@ -1994,9 +2075,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
@@ -2034,6 +2115,7 @@ version = "0.0.0"
dependencies = [
"fst",
"indexmap",
+ "nohash-hasher",
"paths",
"rustc-hash",
"stdx",
@@ -2044,9 +2126,9 @@ name = "vfs-notify"
version = "0.0.0"
dependencies = [
"crossbeam-channel",
- "jod-thread",
"notify",
"paths",
+ "stdx",
"tracing",
"vfs",
"walkdir",
@@ -2054,12 +2136,11 @@ dependencies = [
[[package]]
name = "walkdir"
-version = "2.3.2"
+version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
- "winapi",
"winapi-util",
]
@@ -2117,45 +2198,45 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.1"
+version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "write-json"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 333f03ce2..3050cf764 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
members = ["xtask/", "lib/*", "crates/*"]
exclude = ["crates/proc-macro-test/imp"]
+resolver = "2"
[workspace.package]
rust-version = "1.66"
@@ -74,5 +75,20 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" }
tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
+line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" }
+
# non-local crates
-smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] }
+smallvec = { version = "1.10.0", features = [
+ "const_new",
+ "union",
+ "const_generics",
+] }
+smol_str = "0.2.0"
+nohash-hasher = "0.2.0"
+text-size = "1.1.0"
+# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
+serde = { version = "=1.0.156", features = ["derive"] }
+serde_json = "1.0.94"
+triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
+
+rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
diff --git a/src/tools/rust-analyzer/bench_data/glorious_old_parser b/src/tools/rust-analyzer/bench_data/glorious_old_parser
index 764893daa..f593f2b29 100644
--- a/src/tools/rust-analyzer/bench_data/glorious_old_parser
+++ b/src/tools/rust-analyzer/bench_data/glorious_old_parser
@@ -3808,7 +3808,7 @@ impl<'a> Parser<'a> {
if self.eat_keyword(keywords::Else) || !cond.returns() {
let sp = self.sess.source_map().next_point(lo);
let mut err = self.diagnostic()
- .struct_span_err(sp, "missing condition for `if` statemement");
+ .struct_span_err(sp, "missing condition for `if` statement");
err.span_label(sp, "expected if condition here");
return Err(err)
}
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index f6a1075c1..6001772c8 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -15,6 +15,10 @@ doctest = false
salsa = "0.17.0-pre.2"
rustc-hash = "1.1.0"
+triomphe.workspace = true
+
+la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
+
# local deps
cfg.workspace = true
profile.workspace = true
diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs
index b57f23457..6a3b36b23 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/change.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs
@@ -1,19 +1,21 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.
-use std::{fmt, sync::Arc};
+use std::fmt;
use salsa::Durability;
+use triomphe::Arc;
use vfs::FileId;
-use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};
+use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId};
/// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)]
pub struct Change {
pub roots: Option<Vec<SourceRoot>>,
- pub files_changed: Vec<(FileId, Option<Arc<String>>)>,
+ pub files_changed: Vec<(FileId, Option<Arc<str>>)>,
pub crate_graph: Option<CrateGraph>,
+ pub proc_macros: Option<ProcMacros>,
}
impl fmt::Debug for Change {
@@ -33,7 +35,7 @@ impl fmt::Debug for Change {
}
impl Change {
- pub fn new() -> Change {
+ pub fn new() -> Self {
Change::default()
}
@@ -41,7 +43,7 @@ impl Change {
self.roots = Some(roots);
}
- pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
+ pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
self.files_changed.push((file_id, new_text))
}
@@ -49,6 +51,10 @@ impl Change {
self.crate_graph = Some(graph);
}
+ pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
+ self.proc_macros = Some(proc_macros);
+ }
+
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
let _p = profile::span("RootDatabase::apply_change");
if let Some(roots) = self.roots {
@@ -67,11 +73,14 @@ impl Change {
let source_root = db.source_root(source_root_id);
let durability = durability(&source_root);
// XXX: can't actually remove the file, just reset the text
- let text = text.unwrap_or_default();
+ let text = text.unwrap_or_else(|| Arc::from(""));
db.set_file_text_with_durability(file_id, text, durability)
}
if let Some(crate_graph) = self.crate_graph {
- db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
+ db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
+ }
+ if let Some(proc_macros) = self.proc_macros {
+ db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index 8a7e9dfad..d3abc3870 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -1,24 +1,27 @@
//! A set of high-level utility fixture methods to use in tests.
-use std::{mem, str::FromStr, sync::Arc};
+use std::{mem, str::FromStr, sync};
use cfg::CfgOptions;
use rustc_hash::FxHashMap;
use test_utils::{
- extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
+ extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
+ ESCAPED_CURSOR_MARKER,
};
+use triomphe::Arc;
use tt::token_id::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath};
use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin},
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
- FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt,
- SourceRoot, SourceRootId,
+ FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
+ SourceDatabaseExt, SourceRoot, SourceRootId,
};
pub const WORKSPACE: SourceRootId = SourceRootId(0);
pub trait WithFixture: Default + SourceDatabaseExt + 'static {
+ #[track_caller]
fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -27,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files[0])
}
+ #[track_caller]
fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -35,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files)
}
+ #[track_caller]
fn with_files(ra_fixture: &str) -> Self {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -43,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db
}
+ #[track_caller]
fn with_files_extra_proc_macros(
ra_fixture: &str,
proc_macros: Vec<(String, ProcMacro)>,
@@ -54,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db
}
+ #[track_caller]
fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let offset = range_or_offset.expect_offset();
(db, FilePosition { file_id, offset })
}
+ #[track_caller]
fn with_range(ra_fixture: &str) -> (Self, FileRange) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let range = range_or_offset.expect_range();
(db, FileRange { file_id, range })
}
+ #[track_caller]
fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -100,9 +109,16 @@ impl ChangeFixture {
pub fn parse_with_proc_macros(
ra_fixture: &str,
- mut proc_macros: Vec<(String, ProcMacro)>,
+ mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture {
- let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
+ let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(ra_fixture);
+ let toolchain = toolchain
+ .map(|it| {
+ ReleaseChannel::from_str(&it)
+ .unwrap_or_else(|| panic!("unknown release channel found: {it}"))
+ })
+ .unwrap_or(ReleaseChannel::Stable);
let mut change = Change::new();
let mut files = Vec::new();
@@ -157,16 +173,16 @@ impl ChangeFixture {
meta.edition,
Some(crate_name.clone().into()),
version,
- meta.cfg.clone(),
meta.cfg,
+ Default::default(),
meta.env,
- Ok(Vec::new()),
false,
origin,
meta.target_data_layout
.as_deref()
.map(Arc::from)
.ok_or_else(|| "target_data_layout unset".into()),
+ Some(toolchain),
);
let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none());
@@ -182,7 +198,7 @@ impl ChangeFixture {
default_target_data_layout = meta.target_data_layout;
}
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path);
files.push(file_id);
@@ -197,15 +213,15 @@ impl ChangeFixture {
Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()),
None,
- default_cfg.clone(),
default_cfg,
- Env::default(),
- Ok(Vec::new()),
+ Default::default(),
+ Env::new_for_test_fixture(),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
default_target_data_layout
.map(|x| x.into())
.ok_or_else(|| "target_data_layout unset".into()),
+ Some(toolchain),
);
} else {
for (from, to, prelude) in crate_deps {
@@ -232,7 +248,7 @@ impl ChangeFixture {
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
roots.push(SourceRoot::new_library(fs));
- change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
+ change.change_file(core_file, Some(Arc::from(mini_core.source_code())));
let all_crates = crate_graph.crates_in_topological_order();
@@ -241,13 +257,13 @@ impl ChangeFixture {
Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("core".to_string())),
None,
- CfgOptions::default(),
- CfgOptions::default(),
- Env::default(),
- Ok(Vec::new()),
+ Default::default(),
+ Default::default(),
+ Env::new_for_test_fixture(),
false,
CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(),
+ Some(toolchain),
);
for krate in all_crates {
@@ -257,12 +273,13 @@ impl ChangeFixture {
}
}
+ let mut proc_macros = ProcMacros::default();
if !proc_macro_names.is_empty() {
let proc_lib_file = file_id;
file_id.0 += 1;
- proc_macros.extend(default_test_proc_macros());
- let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros);
+ proc_macro_defs.extend(default_test_proc_macros());
+ let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
let mut fs = FileSet::default();
fs.insert(
proc_lib_file,
@@ -270,7 +287,7 @@ impl ChangeFixture {
);
roots.push(SourceRoot::new_library(fs));
- change.change_file(proc_lib_file, Some(Arc::new(source)));
+ change.change_file(proc_lib_file, Some(Arc::from(source)));
let all_crates = crate_graph.crates_in_topological_order();
@@ -279,14 +296,15 @@ impl ChangeFixture {
Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())),
None,
- CfgOptions::default(),
- CfgOptions::default(),
- Env::default(),
- Ok(proc_macro),
+ Default::default(),
+ Default::default(),
+ Env::new_for_test_fixture(),
true,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
target_layout,
+ Some(toolchain),
);
+ proc_macros.insert(proc_macros_crate, Ok(proc_macro));
for krate in all_crates {
crate_graph
@@ -305,6 +323,7 @@ impl ChangeFixture {
roots.push(root);
change.set_roots(roots);
change.set_crate_graph(crate_graph);
+ change.set_proc_macros(proc_macros);
ChangeFixture { file_position, files, change }
}
@@ -323,7 +342,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
ProcMacro {
name: "identity".into(),
kind: crate::ProcMacroKind::Attr,
- expander: Arc::new(IdentityProcMacroExpander),
+ expander: sync::Arc::new(IdentityProcMacroExpander),
},
),
(
@@ -337,7 +356,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
ProcMacro {
name: "DeriveIdentity".into(),
kind: crate::ProcMacroKind::CustomDerive,
- expander: Arc::new(IdentityProcMacroExpander),
+ expander: sync::Arc::new(IdentityProcMacroExpander),
},
),
(
@@ -351,7 +370,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
ProcMacro {
name: "input_replace".into(),
kind: crate::ProcMacroKind::Attr,
- expander: Arc::new(AttributeInputReplaceProcMacroExpander),
+ expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
},
),
(
@@ -365,7 +384,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
ProcMacro {
name: "mirror".into(),
kind: crate::ProcMacroKind::FuncLike,
- expander: Arc::new(MirrorProcMacroExpander),
+ expander: sync::Arc::new(MirrorProcMacroExpander),
},
),
(
@@ -379,7 +398,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
ProcMacro {
name: "shorten".into(),
kind: crate::ProcMacroKind::FuncLike,
- expander: Arc::new(ShortenProcMacroExpander),
+ expander: sync::Arc::new(ShortenProcMacroExpander),
},
),
]
@@ -428,7 +447,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
let (version, origin) = match b.split_once(':') {
Some(("CratesIo", data)) => match data.split_once(',') {
Some((version, url)) => {
- (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None })
+ (version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None })
}
_ => panic!("Bad crates.io parameter: {data}"),
},
@@ -436,10 +455,9 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
};
(a.to_owned(), origin, Some(version.to_string()))
} else {
- let crate_origin = match &*crate_str {
- "std" => CrateOrigin::Lang(LangCrateOrigin::Std),
- "core" => CrateOrigin::Lang(LangCrateOrigin::Core),
- _ => CrateOrigin::CratesIo { repo: None, name: None },
+ let crate_origin = match LangCrateOrigin::from(&*crate_str) {
+ LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
+ origin => CrateOrigin::Lang(origin),
};
(crate_str, crate_origin, None)
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 43388e915..f2e523675 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -6,14 +6,20 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input.
-use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
+use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
use cfg::CfgOptions;
-use rustc_hash::FxHashMap;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use la_arena::{Arena, Idx};
+use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr;
+use triomphe::Arc;
use tt::token_id::Subtree;
-use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
+use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
+
+// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
+// then the crate for the proc-macro hasn't been build yet as the build data is missing.
+pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
+pub type ProcMacros = FxHashMap<CrateId, ProcMacroLoadResult>;
/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
@@ -79,17 +85,22 @@ impl SourceRoot {
///
/// `CrateGraph` is `!Serialize` by design, see
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
-#[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
+#[derive(Clone, Default)]
pub struct CrateGraph {
- arena: NoHashHashMap<CrateId, CrateData>,
+ arena: Arena<CrateData>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct CrateId(pub u32);
+impl fmt::Debug for CrateGraph {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_map()
+ .entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data)))
+ .finish()
+ }
+}
-impl stdx::hash::NoHashHashable for CrateId {}
+pub type CrateId = Idx<CrateData>;
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CrateName(SmolStr);
impl CrateName {
@@ -130,12 +141,22 @@ impl ops::Deref for CrateName {
/// Origin of the crates. It is used in emitting monikers.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CrateOrigin {
- /// Crates that are from crates.io official registry,
- CratesIo { repo: Option<String>, name: Option<String> },
+ /// Crates that are from the rustc workspace
+ Rustc { name: String },
+ /// Crates that are workspace members,
+ Local { repo: Option<String>, name: Option<String> },
+ /// Crates that are non member libraries.
+ Library { repo: Option<String>, name: String },
/// Crates that are provided by the language, like std, core, proc-macro, ...
Lang(LangCrateOrigin),
}
+impl CrateOrigin {
+ pub fn is_local(&self) -> bool {
+ matches!(self, CrateOrigin::Local { .. })
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LangCrateOrigin {
Alloc,
@@ -173,7 +194,7 @@ impl fmt::Display for LangCrateOrigin {
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CrateDisplayName {
// The name we use to display various paths (with `_`).
crate_name: CrateName,
@@ -249,10 +270,36 @@ pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
pub struct ProcMacro {
pub name: SmolStr,
pub kind: ProcMacroKind,
- pub expander: Arc<dyn ProcMacroExpander>,
+ pub expander: sync::Arc<dyn ProcMacroExpander>,
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum ReleaseChannel {
+ Stable,
+ Beta,
+ Nightly,
+}
+
+impl ReleaseChannel {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ ReleaseChannel::Stable => "stable",
+ ReleaseChannel::Beta => "beta",
+ ReleaseChannel::Nightly => "nightly",
+ }
+ }
+
+ pub fn from_str(str: &str) -> Option<Self> {
+ Some(match str {
+ "" => ReleaseChannel::Stable,
+ "nightly" => ReleaseChannel::Nightly,
+ _ if str.starts_with("beta") => ReleaseChannel::Beta,
+ _ => return None,
+ })
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CrateData {
pub root_file_id: FileId,
pub edition: Edition,
@@ -265,13 +312,15 @@ pub struct CrateData {
/// `Dependency` matters), this name should only be used for UI.
pub display_name: Option<CrateDisplayName>,
pub cfg_options: CfgOptions,
- pub potential_cfg_options: CfgOptions,
- pub target_layout: TargetLayoutLoadResult,
+ /// The cfg options that could be used by the crate
+ pub potential_cfg_options: Option<CfgOptions>,
pub env: Env,
pub dependencies: Vec<Dependency>,
- pub proc_macro: ProcMacroLoadResult,
pub origin: CrateOrigin,
pub is_proc_macro: bool,
+ // FIXME: These things should not be per crate! These are more per workspace crate graph level things
+ pub target_layout: TargetLayoutLoadResult,
+ pub channel: Option<ReleaseChannel>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -290,7 +339,18 @@ pub struct Env {
entries: FxHashMap<String, String>,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+impl Env {
+ pub fn new_for_test_fixture() -> Self {
+ Env {
+ entries: FxHashMap::from_iter([(
+ String::from("__ra_is_test_fixture"),
+ String::from("__ra_is_test_fixture"),
+ )]),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency {
pub crate_id: CrateId,
pub name: CrateName,
@@ -320,12 +380,12 @@ impl CrateGraph {
display_name: Option<CrateDisplayName>,
version: Option<String>,
cfg_options: CfgOptions,
- potential_cfg_options: CfgOptions,
+ potential_cfg_options: Option<CfgOptions>,
env: Env,
- proc_macro: ProcMacroLoadResult,
is_proc_macro: bool,
origin: CrateOrigin,
target_layout: Result<Arc<str>, Arc<str>>,
+ channel: Option<ReleaseChannel>,
) -> CrateId {
let data = CrateData {
root_file_id,
@@ -335,16 +395,44 @@ impl CrateGraph {
cfg_options,
potential_cfg_options,
env,
- proc_macro,
dependencies: Vec::new(),
origin,
target_layout,
is_proc_macro,
+ channel,
};
- let crate_id = CrateId(self.arena.len() as u32);
- let prev = self.arena.insert(crate_id, data);
- assert!(prev.is_none());
- crate_id
+ self.arena.alloc(data)
+ }
+
+ /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
+ /// with the second input.
+ pub fn remove_and_replace(
+ &mut self,
+ id: CrateId,
+ replace_with: CrateId,
+ ) -> Result<(), CyclicDependenciesError> {
+ for (x, data) in self.arena.iter() {
+ if x == id {
+ continue;
+ }
+ for edge in &data.dependencies {
+ if edge.crate_id == id {
+ self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
+ }
+ }
+ }
+ // if everything was ok, start to replace
+ for (x, data) in self.arena.iter_mut() {
+ if x == id {
+ continue;
+ }
+ for edge in &mut data.dependencies {
+ if edge.crate_id == id {
+ edge.crate_id = replace_with;
+ }
+ }
+ }
+ Ok(())
}
pub fn add_dep(
@@ -354,17 +442,26 @@ impl CrateGraph {
) -> Result<(), CyclicDependenciesError> {
let _p = profile::span("add_dep");
- // Check if adding a dep from `from` to `to` creates a cycle. To figure
- // that out, look for a path in the *opposite* direction, from `to` to
- // `from`.
- if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) {
+ self.check_cycle_after_dependency(from, dep.crate_id)?;
+
+ self.arena[from].add_dep(dep);
+ Ok(())
+ }
+
+ /// Check if adding a dep from `from` to `to` creates a cycle. To figure
+ /// that out, look for a path in the *opposite* direction, from `to` to
+ /// `from`.
+ fn check_cycle_after_dependency(
+ &self,
+ from: CrateId,
+ to: CrateId,
+ ) -> Result<(), CyclicDependenciesError> {
+ if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
let err = CyclicDependenciesError { path };
- assert!(err.from().0 == from && err.to().0 == dep.crate_id);
+ assert!(err.from().0 == from && err.to().0 == to);
return Err(err);
}
-
- self.arena.get_mut(&from).unwrap().add_dep(dep);
Ok(())
}
@@ -373,14 +470,20 @@ impl CrateGraph {
}
pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
- self.arena.keys().copied()
+ self.arena.iter().map(|(idx, _)| idx)
+ }
+
+ // FIXME: used for `handle_hack_cargo_workspace`, should be removed later
+ #[doc(hidden)]
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
+ self.arena.iter_mut()
}
/// Returns an iterator over all transitive dependencies of the given crate,
/// including the crate itself.
pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut deps = NoHashHashSet::default();
+ let mut deps = FxHashSet::default();
while let Some(krate) = worklist.pop() {
if !deps.insert(krate) {
@@ -397,11 +500,11 @@ impl CrateGraph {
/// including the crate itself.
pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut rev_deps = NoHashHashSet::default();
+ let mut rev_deps = FxHashSet::default();
rev_deps.insert(of);
- let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default();
- self.arena.iter().for_each(|(&krate, data)| {
+ let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
+ self.arena.iter().for_each(|(krate, data)| {
data.dependencies
.iter()
.for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
@@ -424,9 +527,9 @@ impl CrateGraph {
/// come before the crate itself).
pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
let mut res = Vec::new();
- let mut visited = NoHashHashSet::default();
+ let mut visited = FxHashSet::default();
- for krate in self.arena.keys().copied() {
+ for krate in self.iter() {
go(self, &mut visited, &mut res, krate);
}
@@ -434,7 +537,7 @@ impl CrateGraph {
fn go(
graph: &CrateGraph,
- visited: &mut NoHashHashSet<CrateId>,
+ visited: &mut FxHashSet<CrateId>,
res: &mut Vec<CrateId>,
source: CrateId,
) {
@@ -450,31 +553,56 @@ impl CrateGraph {
// FIXME: this only finds one crate with the given root; we could have multiple
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
- let (&crate_id, _) =
+ let (crate_id, _) =
self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
Some(crate_id)
}
+ pub fn sort_deps(&mut self) {
+ self.arena
+ .iter_mut()
+ .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
+ }
+
/// Extends this crate graph by adding a complete disjoint second crate
- /// graph.
+ /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly.
///
- /// The ids of the crates in the `other` graph are shifted by the return
- /// amount.
- pub fn extend(&mut self, other: CrateGraph) -> u32 {
- let start = self.arena.len() as u32;
- self.arena.extend(other.arena.into_iter().map(|(id, mut data)| {
- let new_id = id.shift(start);
- for dep in &mut data.dependencies {
- dep.crate_id = dep.crate_id.shift(start);
+ /// This will deduplicate the crates of the graph where possible.
+ /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
+ /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
+ pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
+ let topo = other.crates_in_topological_order();
+ let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
+
+ for topo in topo {
+ let crate_data = &mut other.arena[topo];
+ crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
+ crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
+
+ let res = self.arena.iter().find_map(
+ |(id, data)| {
+ if data == crate_data {
+ Some(id)
+ } else {
+ None
+ }
+ },
+ );
+ if let Some(res) = res {
+ id_map.insert(topo, res);
+ } else {
+ let id = self.arena.alloc(crate_data.clone());
+ id_map.insert(topo, id);
}
- (new_id, data)
- }));
- start
+ }
+
+ *proc_macros =
+ mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
}
fn find_path(
&self,
- visited: &mut NoHashHashSet<CrateId>,
+ visited: &mut FxHashSet<CrateId>,
from: CrateId,
to: CrateId,
) -> Option<Vec<CrateId>> {
@@ -500,14 +628,14 @@ impl CrateGraph {
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
// As hacky as it gets.
pub fn patch_cfg_if(&mut self) -> bool {
- let cfg_if = self.hacky_find_crate("cfg_if");
- let std = self.hacky_find_crate("std");
+ // we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
+ let cfg_if =
+ self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
+ let std = self.hacky_find_crate("std").next();
match (cfg_if, std) {
(Some(cfg_if), Some(std)) => {
- self.arena.get_mut(&cfg_if).unwrap().dependencies.clear();
- self.arena
- .get_mut(&std)
- .unwrap()
+ self.arena[cfg_if].dependencies.clear();
+ self.arena[std]
.dependencies
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
true
@@ -516,21 +644,15 @@ impl CrateGraph {
}
}
- fn hacky_find_crate(&self, display_name: &str) -> Option<CrateId> {
- self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name))
+ fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
+ self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
}
}
impl ops::Index<CrateId> for CrateGraph {
type Output = CrateData;
fn index(&self, crate_id: CrateId) -> &CrateData {
- &self.arena[&crate_id]
- }
-}
-
-impl CrateId {
- fn shift(self, amount: u32) -> CrateId {
- CrateId(self.0 + amount)
+ &self.arena[crate_id]
}
}
@@ -632,7 +754,7 @@ impl fmt::Display for CyclicDependenciesError {
mod tests {
use crate::CrateOrigin;
- use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
+ use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
#[test]
fn detect_cyclic_dependency_indirect() {
@@ -642,39 +764,39 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate3 = graph.add_crate_root(
FileId(3u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -695,26 +817,26 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -732,39 +854,39 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate3 = graph.add_crate_root(
FileId(3u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -782,26 +904,26 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 9720db9d8..af204e44e 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -6,18 +6,19 @@ mod input;
mod change;
pub mod fixture;
-use std::{panic, sync::Arc};
+use std::panic;
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
+use triomphe::Arc;
pub use crate::{
change::Change,
input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
- ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
- TargetLayoutLoadResult,
+ ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros,
+ ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
},
};
pub use salsa::{self, Cancelled};
@@ -53,13 +54,13 @@ pub struct FileRange {
pub range: TextRange,
}
-pub const DEFAULT_LRU_CAP: usize = 128;
+pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
pub trait FileLoader {
/// Text of the file.
- fn file_text(&self, file_id: FileId) -> Arc<String>;
+ fn file_text(&self, file_id: FileId) -> Arc<str>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
}
/// Database which stores all significant input facts: source code and project
@@ -73,6 +74,10 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
/// The crate graph.
#[salsa::input]
fn crate_graph(&self) -> Arc<CrateGraph>;
+
+ /// The crate graph.
+ #[salsa::input]
+ fn proc_macros(&self) -> Arc<ProcMacros>;
}
fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
@@ -86,7 +91,7 @@ fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFil
#[salsa::query_group(SourceDatabaseExtStorage)]
pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
- fn file_text(&self, file_id: FileId) -> Arc<String>;
+ fn file_text(&self, file_id: FileId) -> Arc<str>;
/// Path to a file, relative to the root of its source root.
/// Source root of the file.
#[salsa::input]
@@ -95,10 +100,10 @@ pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
- fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
+ fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
}
-fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
+fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
let graph = db.crate_graph();
let res = graph
.iter()
@@ -114,7 +119,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHas
pub struct FileLoaderDelegate<T>(pub T);
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
SourceDatabaseExt::file_text(self.0, file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
@@ -124,7 +129,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
source_root.resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
let _p = profile::span("relevant_crates");
let source_root = self.0.file_source_root(file_id);
self.0.source_root_crates(source_root)
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 30709c968..495119d55 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -86,7 +86,7 @@ impl CfgOptions {
}
}
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
enable: Vec<CfgAtom>,
diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
index 609d18c4e..3f6671b1c 100644
--- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
@@ -16,9 +16,8 @@ crossbeam-channel = "0.5.5"
tracing = "0.1.37"
cargo_metadata = "0.15.0"
rustc-hash = "1.1.0"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.86"
-jod-thread = "0.1.2"
+serde_json.workspace = true
+serde.workspace = true
command-group = "2.0.1"
# local deps
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index accb14a51..fbb943ccb 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig {
pub struct FlycheckHandle {
// XXX: drop order is significant
sender: Sender<StateChange>,
- _thread: jod_thread::JoinHandle,
+ _thread: stdx::thread::JoinHandle,
id: usize,
}
@@ -90,7 +90,7 @@ impl FlycheckHandle {
) -> FlycheckHandle {
let actor = FlycheckActor::new(id, sender, config, workspace_root);
let (sender, receiver) = unbounded::<StateChange>();
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("Flycheck".to_owned())
.spawn(move || actor.run(receiver))
.expect("failed to spawn thread");
@@ -395,7 +395,7 @@ struct CargoHandle {
/// The handle to the actual cargo process. As we cannot cancel directly from with
/// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild,
- thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
+ thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>,
}
@@ -409,7 +409,7 @@ impl CargoHandle {
let (sender, receiver) = unbounded();
let actor = CargoActor::new(sender, stdout, stderr);
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("CargoHandle".to_owned())
.spawn(move || actor.run())
.expect("failed to spawn thread");
@@ -485,7 +485,7 @@ impl CargoActor {
error.push_str(line);
error.push('\n');
- return false;
+ false
};
let output = streaming_output(
self.stdout,
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 31d4018d2..83c705164 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
[dependencies]
anymap = "1.0.0-beta.2"
arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
cov-mark = "2.0.0-pre.1"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.4.0", features = ["raw-api"] }
@@ -29,6 +29,7 @@ once_cell = "1.17.0"
rustc-hash = "1.1.0"
smallvec.workspace = true
tracing = "0.1.35"
+triomphe.workspace = true
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 200072c17..bab3bbc23 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -1,6 +1,11 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{hash::Hash, ops, sync::Arc};
+pub mod builtin;
+
+#[cfg(test)]
+mod tests;
+
+use std::{hash::Hash, ops};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -16,14 +21,16 @@ use syntax::{
ast::{self, HasAttrs, IsString},
AstPtr, AstToken, SmolStr, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
+ lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
- AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
- VariantId,
+ AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
+ LocalFieldId, Lookup, MacroId, VariantId,
};
/// Holds documentation
@@ -88,6 +95,7 @@ impl Attrs {
db: &dyn DefDatabase,
e: EnumId,
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
+ let _p = profile::span("variants_attrs_query");
// FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
let mut res = ArenaMap::default();
@@ -114,6 +122,7 @@ impl Attrs {
db: &dyn DefDatabase,
v: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
+ let _p = profile::span("fields_attrs_query");
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default();
@@ -175,13 +184,13 @@ impl Attrs {
Arc::new(res)
}
+}
+impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
-}
-impl Attrs {
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
@@ -193,6 +202,7 @@ impl Attrs {
None => Some(first),
}
}
+
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() {
None => true,
@@ -204,6 +214,10 @@ impl Attrs {
self.by_key("lang").string_value()
}
+ pub fn lang_item(&self) -> Option<LangItem> {
+ self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
+ }
+
pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
let indent = doc_indent(self);
@@ -238,6 +252,14 @@ impl Attrs {
})
}
+ pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
+ self.by_key("doc").tt_values().map(DocExpr::parse)
+ }
+
+ pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+ self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+ }
+
pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists()
}
@@ -249,10 +271,120 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
+
+ pub fn is_unstable(&self) -> bool {
+ self.by_key("unstable").exists()
+ }
+}
+
+use std::slice::Iter as SliceIter;
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum DocAtom {
+ /// eg. `#[doc(hidden)]`
+ Flag(SmolStr),
+ /// eg. `#[doc(alias = "x")]`
+ ///
+ /// Note that a key can have multiple values that are all considered "active" at the same time.
+ /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
+ KeyValue { key: SmolStr, value: SmolStr },
+}
+
+// Adapted from `CfgExpr` parsing code
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
+pub enum DocExpr {
+ Invalid,
+ /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+ Atom(DocAtom),
+ /// eg. `#[doc(alias("x", "y"))]`
+ Alias(Vec<SmolStr>),
+}
+
+impl From<DocAtom> for DocExpr {
+ fn from(atom: DocAtom) -> Self {
+ DocExpr::Atom(atom)
+ }
+}
+
+impl DocExpr {
+ fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
+ next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
+ }
+
+ pub fn aliases(&self) -> &[SmolStr] {
+ match self {
+ DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+ std::slice::from_ref(value)
+ }
+ DocExpr::Alias(aliases) => aliases,
+ _ => &[],
+ }
+ }
+}
+
+fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+ let name = match it.next() {
+ None => return None,
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+ Some(_) => return Some(DocExpr::Invalid),
+ };
+
+ // Peek
+ let ret = match it.as_slice().first() {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+ match it.as_slice().get(1) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+ it.next();
+ it.next();
+ // FIXME: escape? raw string?
+ let value =
+ SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
+ DocAtom::KeyValue { key: name, value }.into()
+ }
+ _ => return Some(DocExpr::Invalid),
+ }
+ }
+ Some(tt::TokenTree::Subtree(subtree)) => {
+ it.next();
+ let subs = parse_comma_sep(subtree);
+ match name.as_str() {
+ "alias" => DocExpr::Alias(subs),
+ _ => DocExpr::Invalid,
+ }
+ }
+ _ => DocAtom::Flag(name).into(),
+ };
+
+ // Eat comma separator
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
+ if punct.char == ',' {
+ it.next();
+ }
+ }
+ Some(ret)
+}
+
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+ subtree
+ .token_trees
+ .iter()
+ .filter_map(|tt| match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+ // FIXME: escape? raw string?
+ Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
+ }
+ _ => None,
+ })
+ .collect()
}
impl AttrsWithOwner {
- pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
+ pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+ Self { attrs: db.attrs(owner), owner }
+ }
+
+ pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+ let _p = profile::span("attrs_query");
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
let raw_attrs = match def {
AttrDefId::ModuleId(module) => {
@@ -286,31 +418,29 @@ impl AttrsWithOwner {
}
}
AttrDefId::FieldId(it) => {
- return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
+ return db.fields_attrs(it.parent)[it.local_id].clone();
}
AttrDefId::EnumVariantId(it) => {
- return Self {
- attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
- owner: def,
- };
+ return db.variants_attrs(it.parent)[it.local_id].clone();
}
+ // FIXME: DRY this up
AttrDefId::AdtId(it) => match it {
- AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
},
- AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::MacroId(it) => match it {
- MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
},
- AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
@@ -331,11 +461,11 @@ impl AttrsWithOwner {
RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
}
},
- AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
};
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
- Self { attrs: Attrs(attrs), owner: def }
+ Attrs(attrs)
}
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -371,7 +501,7 @@ impl AttrsWithOwner {
AttrDefId::FieldId(id) => {
let map = db.fields_attrs_source_map(id.parent);
let file_id = id.parent.file_id(db);
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
let owner = match &map[id.local_id] {
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
@@ -379,28 +509,28 @@ impl AttrsWithOwner {
InFile::new(file_id, owner)
}
AttrDefId::AdtId(adt) => match adt {
- AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AdtId::StructId(id) => any_has_attrs(db, id),
+ AdtId::UnionId(id) => any_has_attrs(db, id),
+ AdtId::EnumId(id) => any_has_attrs(db, id),
},
- AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::FunctionId(id) => any_has_attrs(db, id),
AttrDefId::EnumVariantId(id) => {
let map = db.variants_attrs_source_map(id.parent);
let file_id = id.parent.lookup(db).id.file_id();
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
}
- AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::StaticId(id) => any_has_attrs(db, id),
+ AttrDefId::ConstId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
+ AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
AttrDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ MacroId::Macro2Id(id) => any_has_attrs(db, id),
+ MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+ MacroId::ProcMacroId(id) => any_has_attrs(db, id),
},
- AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ImplId(id) => any_has_attrs(db, id),
AttrDefId::GenericParamId(id) => match id {
GenericParamId::ConstParamId(id) => id
.parent()
@@ -415,7 +545,7 @@ impl AttrsWithOwner {
.child_source(db)
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
},
- AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
};
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
.nth(2);
match name {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
_ => None
}
})
}
}
-fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
+fn any_has_attrs(
+ db: &dyn DefDatabase,
+ id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
+) -> InFile<ast::AnyHasAttrs> {
+ id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
let tree = id.item_tree(db);
let mod_item = N::id_to_mod_item(id.value);
tree.raw_attrs(mod_item.into()).clone()
}
+fn attrs_from_item_tree_loc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = ItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
+fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = AssocItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
pub(crate) fn variants_attrs_source_map(
db: &dyn DefDatabase,
def: EnumId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index f7c1e683d..cead64a33 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -2,7 +2,7 @@
//!
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
//!
-//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7.
+//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
//!
//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -108,7 +108,7 @@ macro_rules! experimental {
};
}
-/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
+/// Attributes that have a special meaning to rustc or rustdoc.
#[rustfmt::skip]
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================
@@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
ungated!(
should_panic, Normal,
- template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
+ template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
),
// FIXME(Centril): This can be used on stable but shouldn't.
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
@@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Lints:
ungated!(
- warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
gated!(
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
lint_reasons, experimental!(expect)
),
ungated!(
- forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
gated!(
@@ -181,30 +185,28 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ABI, linking, symbols, and FFI
ungated!(
link, Normal,
- template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
+ template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
DuplicatesOk,
),
ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_link, Normal, template!(Word), WarnFollowing),
- ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
+ ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true),
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
+ ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding),
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
- const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
- const_eval_limit, experimental!(const_eval_limit)
- ),
- gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)
),
// Entry point:
+ gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
ungated!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
@@ -226,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
- ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
+ ungated!(
+ target_feature, Normal, template!(List: r#"enable = "name""#),
+ DuplicatesOk, @only_local: true,
+ ),
ungated!(track_caller, Normal, template!(Word), WarnFollowing),
+ ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
gated!(
no_sanitize, Normal,
- template!(List: "address, memory, thread"), DuplicatesOk,
+ template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize)
),
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
@@ -239,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
),
+ // Debugging
+ ungated!(
+ debugger_visualizer, Normal,
+ template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk
+ ),
+
// ==========================================================================
// Unstable attributes:
// ==========================================================================
- // RFC #3191: #[debugger_visualizer] support
- gated!(
- debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
- DuplicatesOk, experimental!(debugger_visualizer)
- ),
-
// Linking:
- gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
gated!(
- link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
- experimental!(link_ordinal)
+ naked, Normal, template!(Word), WarnFollowing, @only_local: true,
+ naked_functions, experimental!(naked)
),
// Plugins:
- // XXX Modified for use in rust-analyzer
// BuiltinAttribute {
// name: sym::plugin,
// only_local: false,
@@ -274,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// cfg_fn!(plugin)
// ),
// },
- BuiltinAttribute {
- name: "plugin",
- template: template!(List: "name"),
- },
// Testing:
gated!(
@@ -286,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// RFC #1268
gated!(
- marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker)
+ marker, Normal, template!(Word), WarnFollowing, @only_local: true,
+ marker_trait_attr, experimental!(marker)
),
gated!(
thread_local, Normal, template!(Word), WarnFollowing,
@@ -298,11 +299,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
experimental!(optimize),
),
- // RFC 2867
- gated!(
- instruction_set, Normal, template!(List: "set"), ErrorPreceding,
- isa_attribute, experimental!(instruction_set)
- ),
gated!(
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
@@ -310,10 +306,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
gated!(
- register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
- experimental!(register_attr),
- ),
- gated!(
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
experimental!(register_tool),
),
@@ -325,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2632
gated!(
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
- "`const` is a temporary placeholder for marking a trait that is suitable for `const` \
+ "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
),
@@ -335,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
experimental!(deprecated_safe),
),
+ // `#[collapse_debuginfo]`
+ gated!(
+ collapse_debuginfo, Normal, template!(Word), WarnFollowing,
+ experimental!(collapse_debuginfo)
+ ),
+
+ // RFC 2397
+ gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
+
+ // `#[cfi_encoding = ""]`
+ gated!(
+ cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
+ experimental!(cfi_encoding)
+ ),
+
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
- ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk),
+ ungated!(
+ feature, CrateLevel,
+ template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true,
+ ),
// DuplicatesOk since it has its own validation
ungated!(
- stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
+ stable, Normal,
+ template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true,
),
ungated!(
unstable, Normal,
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
),
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
+ ungated!(
+ rustc_const_stable, Normal,
+ template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true,
+ ),
+ ungated!(
+ rustc_default_body_unstable, Normal,
+ template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk
+ ),
gated!(
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
"allow_internal_unstable side-steps feature gating and stability checks",
@@ -364,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
"allow_internal_unsafe side-steps the unsafe_code lint",
),
+ ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
+ rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
+ "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
+ through unstable paths"),
// ==========================================================================
// Internal attributes: Type system related:
@@ -381,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
- gated!(
- alloc_error_handler, Normal, template!(Word), WarnFollowing,
- experimental!(alloc_error_handler)
- ),
+ rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
experimental!(default_lib_allocator),
@@ -465,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
// to assist in changes to diagnostic APIs.
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint on fields
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Const related:
@@ -508,8 +534,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"language items are subject to change",
),
rustc_attr!(
- rustc_pass_by_value, Normal,
- template!(Word), ErrorFollowing,
+ rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
),
rustc_attr!(
@@ -517,10 +542,18 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
),
rustc_attr!(
+ rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
+ "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
+ ),
+ rustc_attr!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
rustc_attr!(
+ rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+ "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+ ),
+ rustc_attr!(
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
@@ -531,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
and it is only intended to be used in `alloc`."
),
- // modified for r-a
- // BuiltinAttribute {
- // name: sym::rustc_diagnostic_item,
- // // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
- // only_local: false,
- // type_: Normal,
- // template: template!(NameValueStr: "name"),
- // duplicates: ErrorFollowing,
- // gate: Gated(
- // Stability::Unstable,
- // sym::rustc_attrs,
- // "diagnostic items compiler internal support for linting",
- // cfg_fn!(rustc_attrs),
- // ),
- // },
BuiltinAttribute {
+ // name: sym::rustc_diagnostic_item,
name: "rustc_diagnostic_item",
+ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
+ // only_local: false,
+ // type_: Normal,
template: template!(NameValueStr: "name"),
+ // duplicates: ErrorFollowing,
+ // gate: Gated(
+ // Stability::Unstable,
+ // sym::rustc_attrs,
+ // "diagnostic items compiler internal support for linting",
+ // cfg_fn!(rustc_attrs),
+ // ),
},
gated!(
// Used in resolve:
@@ -572,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
for reserving for `for<T> From<!> for T` impl"
),
rustc_attr!(
- rustc_test_marker, Normal, template!(Word), WarnFollowing,
+ rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
"the `#[rustc_test_marker]` attribute is used internally to track tests",
),
rustc_attr!(
@@ -598,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
definition of a trait, it's currently in experimental form and should be changed before \
being exposed outside of the std"
),
+ rustc_attr!(
+ rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
+ r#"`rustc_doc_primitive` is a rustc internal attribute"#,
+ ),
// ==========================================================================
// Internal attributes, Testing:
// ==========================================================================
+ rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
@@ -643,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
+ gated!(
+ custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
+ ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite",
+ ),
rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
new file mode 100644
index 000000000..e4c8d446a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
@@ -0,0 +1,40 @@
+//! This module contains tests for doc-expression parsing.
+//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+
+use mbe::syntax_node_to_token_tree;
+use syntax::{ast, AstNode};
+
+use crate::attr::{DocAtom, DocExpr};
+
+fn assert_parse_result(input: &str, expected: DocExpr) {
+ let (tt, _) = {
+ let source_file = ast::SourceFile::parse(input).ok().unwrap();
+ let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ syntax_node_to_token_tree(tt.syntax())
+ };
+ let cfg = DocExpr::parse(&tt);
+ assert_eq!(cfg, expected);
+}
+
+#[test]
+fn test_doc_expr_parser() {
+ assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
+
+ assert_parse_result(
+ r#"#![doc(alias = "foo")]"#,
+ DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
+ );
+
+ assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
+ assert_parse_result(
+ r#"#![doc(alias("foo", "bar", "baz"))]"#,
+ DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
+ );
+
+ assert_parse_result(
+ r#"
+ #[doc(alias("Bar", "Qux"))]
+ struct Foo;"#,
+ DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index b70e658ef..94dc39b11 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -6,267 +6,30 @@ mod tests;
pub mod scope;
mod pretty;
-use std::{ops::Index, sync::Arc};
+use std::ops::Index;
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
-use drop_bomb::DropBomb;
use either::Either;
-use hir_expand::{
- attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
-};
+use hir_expand::{name::Name, HirFileId, InFile};
use la_arena::{Arena, ArenaMap};
-use limit::Limit;
use profile::Count;
use rustc_hash::FxHashMap;
-use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, AstPtr, SyntaxNodePtr};
+use triomphe::Arc;
use crate::{
- attr::Attrs,
db::DefDatabase,
- expr::{
+ expander::Expander,
+ hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
},
- item_scope::BuiltinShadowMode,
- macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
src::{HasChildSource, HasSource},
- AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
- UnresolvedMacro,
+ BlockId, DefWithBodyId, HasModule, Lookup,
};
-pub use lower::LowerCtx;
-
-/// A subset of Expander that only deals with cfg attributes. We only need it to
-/// avoid cyclic queries in crate def map during enum processing.
-#[derive(Debug)]
-pub(crate) struct CfgExpander {
- cfg_options: CfgOptions,
- hygiene: Hygiene,
- krate: CrateId,
-}
-
-#[derive(Debug)]
-pub struct Expander {
- cfg_expander: CfgExpander,
- def_map: Arc<DefMap>,
- current_file_id: HirFileId,
- module: LocalModuleId,
- /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
- recursion_depth: usize,
-}
-
-impl CfgExpander {
- pub(crate) fn new(
- db: &dyn DefDatabase,
- current_file_id: HirFileId,
- krate: CrateId,
- ) -> CfgExpander {
- let hygiene = Hygiene::new(db.upcast(), current_file_id);
- let cfg_options = db.crate_graph()[krate].cfg_options.clone();
- CfgExpander { cfg_options, hygiene, krate }
- }
-
- pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
- Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
- }
-
- pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
- let attrs = self.parse_attrs(db, owner);
- attrs.is_cfg_enabled(&self.cfg_options)
- }
-}
-
-impl Expander {
- pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
- let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
- let def_map = module.def_map(db);
- Expander {
- cfg_expander,
- def_map,
- current_file_id,
- module: module.local_id,
- recursion_depth: 0,
- }
- }
-
- pub fn enter_expand<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- macro_call: ast::MacroCall,
- ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
- let mut unresolved_macro_err = None;
-
- let result = self.within_limit(db, |this| {
- let macro_call = InFile::new(this.current_file_id, &macro_call);
-
- let resolver =
- |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
-
- let mut err = None;
- let call_id = match macro_call.as_call_id_with_errors(
- db,
- this.def_map.krate(),
- resolver,
- &mut |e| {
- err.get_or_insert(e);
- },
- ) {
- Ok(call_id) => call_id,
- Err(resolve_err) => {
- unresolved_macro_err = Some(resolve_err);
- return ExpandResult { value: None, err: None };
- }
- };
- ExpandResult { value: call_id.ok(), err }
- });
-
- if let Some(err) = unresolved_macro_err {
- Err(err)
- } else {
- Ok(result)
- }
- }
-
- pub fn enter_expand_id<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- ) -> ExpandResult<Option<(Mark, T)>> {
- self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
- }
-
- fn enter_expand_inner(
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- mut err: Option<ExpandError>,
- ) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
- if err.is_none() {
- err = db.macro_expand_error(call_id);
- }
-
- let file_id = call_id.as_file();
-
- let raw_node = match db.parse_or_expand(file_id) {
- Some(it) => it,
- None => {
- // Only `None` if the macro expansion produced no usable AST.
- if err.is_none() {
- tracing::warn!("no error despite `parse_or_expand` failing");
- }
-
- return ExpandResult::only_err(err.unwrap_or_else(|| {
- ExpandError::Other("failed to parse macro invocation".into())
- }));
- }
- };
-
- ExpandResult { value: Some((file_id, raw_node)), err }
- }
-
- pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
- self.current_file_id = mark.file_id;
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
- // depth only when we get out of the tree.
- if !self.current_file_id.is_macro() {
- self.recursion_depth = 0;
- }
- } else {
- self.recursion_depth -= 1;
- }
- mark.bomb.defuse();
- }
-
- pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
- InFile { file_id: self.current_file_id, value }
- }
-
- pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
- self.cfg_expander.parse_attrs(db, owner)
- }
-
- pub(crate) fn cfg_options(&self) -> &CfgOptions {
- &self.cfg_expander.cfg_options
- }
-
- pub fn current_file_id(&self) -> HirFileId {
- self.current_file_id
- }
-
- fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
- let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
- Path::from_src(path, &ctx)
- }
-
- fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
- self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros()
- }
-
- fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
- let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
-
- #[cfg(not(test))]
- return Limit::new(limit);
-
- // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
- #[cfg(test)]
- return Limit::new(std::cmp::min(32, limit));
- }
-
- fn within_limit<F, T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- op: F,
- ) -> ExpandResult<Option<(Mark, T)>>
- where
- F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
- {
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. We should
- // stop expanding other macro calls in this tree, or else this may result in
- // exponential number of macro expansions, leading to a hang.
- //
- // The overflow error should have been reported when it occurred (see the next branch),
- // so don't return overflow error here to avoid diagnostics duplication.
- cov_mark::hit!(overflow_but_not_me);
- return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
- } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
- self.recursion_depth = usize::MAX;
- cov_mark::hit!(your_stack_belongs_to_me);
- return ExpandResult::only_err(ExpandError::Other(
- "reached recursion limit during macro expansion".into(),
- ));
- }
-
- let ExpandResult { value, err } = op(self);
- let Some(call_id) = value else {
- return ExpandResult { value: None, err };
- };
-
- Self::enter_expand_inner(db, call_id, err).map(|value| {
- value.and_then(|(new_file_id, node)| {
- let node = T::cast(node)?;
-
- self.recursion_depth += 1;
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
- let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id);
- let mark =
- Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
- Some((mark, node))
- })
- })
- }
-}
-
-#[derive(Debug)]
-pub struct Mark {
- file_id: HirFileId,
- bomb: DropBomb,
-}
-
/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
@@ -274,6 +37,9 @@ pub struct Body {
pub pats: Arena<Pat>,
pub bindings: Arena<Binding>,
pub labels: Arena<Label>,
+ /// Id of the closure/generator that owns the corresponding binding. If a binding is owned by the
+ /// top level expression, it will not be listed in here.
+ pub binding_owners: FxHashMap<BindingId, ExprId>,
/// The patterns for the function's parameters. While the parameter types are
/// part of the function signature, the patterns are not (they don't change
/// the external type of the function).
@@ -343,6 +109,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+ UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+ UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
}
impl Body {
@@ -353,45 +121,51 @@ impl Body {
let _p = profile::span("body_with_source_map_query");
let mut params = None;
- let (file_id, module, body) = match def {
- DefWithBodyId::FunctionId(f) => {
- let f = f.lookup(db);
- let src = f.source(db);
- params = src.value.param_list().map(|param_list| {
- let item_tree = f.id.item_tree(db);
- let func = &item_tree[f.id.value];
- let krate = f.container.module(db).krate;
- let crate_graph = db.crate_graph();
- (
- param_list,
- func.params.clone().map(move |param| {
- item_tree
- .attrs(db, krate, param.into())
- .is_cfg_enabled(&crate_graph[krate].cfg_options)
- }),
- )
- });
- (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
- }
- DefWithBodyId::ConstId(c) => {
- let c = c.lookup(db);
- let src = c.source(db);
- (src.file_id, c.module(db), src.value.body())
- }
- DefWithBodyId::StaticId(s) => {
- let s = s.lookup(db);
- let src = s.source(db);
- (src.file_id, s.module(db), src.value.body())
- }
- DefWithBodyId::VariantId(v) => {
- let e = v.parent.lookup(db);
- let src = v.parent.child_source(db);
- let variant = &src.value[v.local_id];
- (src.file_id, e.container, variant.expr())
+ let mut is_async_fn = false;
+ let InFile { file_id, value: body } = {
+ match def {
+ DefWithBodyId::FunctionId(f) => {
+ let data = db.function_data(f);
+ let f = f.lookup(db);
+ let src = f.source(db);
+ params = src.value.param_list().map(|param_list| {
+ let item_tree = f.id.item_tree(db);
+ let func = &item_tree[f.id.value];
+ let krate = f.container.module(db).krate;
+ let crate_graph = db.crate_graph();
+ (
+ param_list,
+ func.params.clone().map(move |param| {
+ item_tree
+ .attrs(db, krate, param.into())
+ .is_cfg_enabled(&crate_graph[krate].cfg_options)
+ }),
+ )
+ });
+ is_async_fn = data.has_async_kw();
+ src.map(|it| it.body().map(ast::Expr::from))
+ }
+ DefWithBodyId::ConstId(c) => {
+ let c = c.lookup(db);
+ let src = c.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::StaticId(s) => {
+ let s = s.lookup(db);
+ let src = s.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::VariantId(v) => {
+ let src = v.parent.child_source(db);
+ src.map(|it| it[v.local_id].expr())
+ }
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
}
};
+ let module = def.module(db);
let expander = Expander::new(db, file_id, module);
- let (mut body, source_map) = Body::new(db, expander, params, body);
+ let (mut body, source_map) =
+ Body::new(db, def, expander, params, body, module.krate, is_async_fn);
body.shrink_to_fit();
(Arc::new(body), Arc::new(source_map))
@@ -406,46 +180,65 @@ impl Body {
&'a self,
db: &'a dyn DefDatabase,
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
- self.block_scopes
- .iter()
- .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
+ self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
}
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
pretty::print_body_hir(db, self, owner)
}
+ pub fn pretty_print_expr(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ expr: ExprId,
+ ) -> String {
+ pretty::print_expr_hir(db, self, owner, expr)
+ }
+
fn new(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
- lower::lower(db, expander, params, body)
+ lower::lower(db, owner, expander, params, body, krate, is_async_fn)
}
fn shrink_to_fit(&mut self) {
- let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
- self;
+ let Self {
+ _c: _,
+ body_expr: _,
+ block_scopes,
+ exprs,
+ labels,
+ params,
+ pats,
+ bindings,
+ binding_owners,
+ } = self;
block_scopes.shrink_to_fit();
exprs.shrink_to_fit();
labels.shrink_to_fit();
params.shrink_to_fit();
pats.shrink_to_fit();
bindings.shrink_to_fit();
+ binding_owners.shrink_to_fit();
}
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
self.walk_pats(pat_id, &mut |pat| {
- if let Pat::Bind { id, .. } = pat {
+ if let Pat::Bind { id, .. } = &self[pat] {
f(*id);
}
});
}
- pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+ pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
let pat = &self[pat_id];
- f(pat);
match pat {
Pat::Range { .. }
| Pat::Lit(..)
@@ -455,21 +248,37 @@ impl Body {
| Pat::Missing => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
- self.walk_pats(subpat, f);
+ f(subpat);
}
}
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
- args.iter().copied().for_each(|p| self.walk_pats(p, f));
+ args.iter().copied().for_each(|p| f(p));
}
- Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
+ Pat::Ref { pat, .. } => f(*pat),
Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
- total_iter.copied().for_each(|p| self.walk_pats(p, f));
+ total_iter.copied().for_each(|p| f(p));
}
Pat::Record { args, .. } => {
- args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
+ args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
+ }
+ Pat::Box { inner } => f(*inner),
+ }
+ }
+
+ pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
+ f(pat_id);
+ self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
+ }
+
+ pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
+ match self.binding_owners.get(&binding) {
+ Some(x) => {
+ // We assign expression ids in a way that outer closures will receive
+ // a lower id
+ x.into_raw() < relative_to.into_raw()
}
- Pat::Box { inner } => self.walk_pats(*inner, f),
+ None => true,
}
}
}
@@ -484,6 +293,7 @@ impl Default for Body {
labels: Default::default(),
params: Default::default(),
block_scopes: Default::default(),
+ binding_owners: Default::default(),
_c: Default::default(),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index fedaf3955..b375ec63a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1,120 +1,148 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
-use std::{mem, sync::Arc};
+use std::mem;
+use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
- hygiene::Hygiene,
name::{name, AsName, Name},
- AstId, ExpandError, HirFileId, InFile,
+ AstId, ExpandError, InFile,
};
use intern::Interned;
-use la_arena::Arena;
-use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
- self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
+ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
};
+use triomphe::Arc;
use crate::{
- adt::StructKind,
- body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
- body::{BodyDiagnostic, ExprSource, PatSource},
- builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+ body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ data::adt::StructKind,
db::DefDatabase,
- expr::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
- FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement,
+ expander::Expander,
+ hir::{
+ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
+ ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
+ lang_item::LangItem,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
+ AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
-pub struct LowerCtx<'a> {
- pub db: &'a dyn DefDatabase,
- hygiene: Hygiene,
- ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
- pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
- LowerCtx {
- db,
- hygiene: Hygiene::new(db.upcast(), file_id),
- ast_id_map: Some((file_id, OnceCell::new())),
- }
- }
-
- pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
- LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
- }
-
- pub(crate) fn hygiene(&self) -> &Hygiene {
- &self.hygiene
- }
-
- pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
- Path::from_src(ast, self)
- }
-
- pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
- let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
- let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
- Some(InFile::new(file_id, ast_id_map.ast_id(item)))
- }
-}
-
pub(super) fn lower(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
ExprCollector {
db,
+ owner,
+ krate,
+ def_map: expander.module.def_map(db),
source_map: BodySourceMap::default(),
ast_id_map: db.ast_id_map(expander.current_file_id),
body: Body {
- exprs: Arena::default(),
- pats: Arena::default(),
- bindings: Arena::default(),
- labels: Arena::default(),
+ exprs: Default::default(),
+ pats: Default::default(),
+ bindings: Default::default(),
+ binding_owners: Default::default(),
+ labels: Default::default(),
params: Vec::new(),
body_expr: dummy_expr_id(),
block_scopes: Vec::new(),
_c: Count::new(),
},
expander,
+ current_try_block_label: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
+ label_ribs: Vec::new(),
+ current_binding_owner: None,
}
- .collect(params, body)
+ .collect(params, body, is_async_fn)
}
struct ExprCollector<'a> {
db: &'a dyn DefDatabase,
expander: Expander,
+ owner: DefWithBodyId,
+ def_map: Arc<DefMap>,
ast_id_map: Arc<AstIdMap>,
+ krate: CrateId,
body: Body,
source_map: BodySourceMap,
+
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
+
+ current_try_block_label: Option<LabelId>,
+ // points to the expression that a try expression will target (replaces current_try_block_label)
+ // catch_scope: Option<ExprId>,
+ // points to the expression that an unlabeled control flow will target
+ // loop_scope: Option<ExprId>,
+ // needed to diagnose non label control flow in while conditions
+ // is_in_loop_condition: bool,
+
+ // resolution
+ label_ribs: Vec<LabelRib>,
+ current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+ kind: RibKind,
+ // Once we handle macro hygiene this will need to be a map
+ label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+ fn new(kind: RibKind) -> Self {
+ LabelRib { kind, label: None }
+ }
+ fn new_normal(label: (Name, LabelId)) -> Self {
+ LabelRib { kind: RibKind::Normal, label: Some(label) }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+ Normal,
+ Closure,
+ Constant,
+}
+
+impl RibKind {
+ /// This rib forbids referring to labels defined in upwards ribs.
+ fn is_label_barrier(self) -> bool {
+ match self {
+ RibKind::Normal => false,
+ RibKind::Closure | RibKind::Constant => true,
+ }
+ }
}
#[derive(Debug, Default)]
struct BindingList {
map: FxHashMap<Name, BindingId>,
+ is_used: FxHashMap<BindingId, bool>,
+ reject_new: bool,
}
impl BindingList {
@@ -124,7 +152,27 @@ impl BindingList {
name: Name,
mode: BindingAnnotation,
) -> BindingId {
- *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+ let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
+ if ec.body.bindings[id].mode != mode {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+ }
+ self.check_is_used(ec, id);
+ id
+ }
+
+ fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) {
+ match self.is_used.get(&id) {
+ None => {
+ if self.reject_new {
+ ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ Some(true) => {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+ }
+ Some(false) => {}
+ }
+ self.is_used.insert(id, true);
}
}
@@ -133,13 +181,14 @@ impl ExprCollector<'_> {
mut self,
param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
if let Some((param_list, mut attr_enabled)) = param_list {
if let Some(self_param) =
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
{
let ptr = AstPtr::new(&self_param);
- let binding_id = self.alloc_binding(
+ let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
name![self],
BindingAnnotation::new(
self_param.mut_token().is_some() && self_param.amp_token().is_none(),
@@ -152,72 +201,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
- for pat in param_list
- .params()
- .zip(attr_enabled)
- .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+ for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
{
- let param_pat = self.collect_pat(pat);
+ let param_pat = self.collect_pat_top(param.pat());
self.body.params.push(param_pat);
}
};
+ self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+ if is_async_fn {
+ match body {
+ Some(e) => {
+ let expr = this.collect_expr(e);
+ this.alloc_expr_desugared(Expr::Async {
+ id: None,
+ statements: Box::new([]),
+ tail: Some(expr),
+ })
+ }
+ None => this.missing_expr(),
+ }
+ } else {
+ this.collect_expr_opt(body)
+ }
+ });
- self.body.body_expr = self.collect_expr_opt(body);
(self.body, self.source_map)
}
fn ctx(&self) -> LowerCtx<'_> {
- LowerCtx::new(self.db, self.expander.current_file_id)
- }
-
- fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
- let src = self.expander.to_source(ptr);
- let id = self.make_expr(expr, src.clone());
- self.source_map.expr_map.insert(src, id);
- id
- }
- // desugared exprs don't have ptr, that's wrong and should be fixed
- // somehow.
- fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
- self.body.exprs.alloc(expr)
- }
- fn missing_expr(&mut self) -> ExprId {
- self.alloc_expr_desugared(Expr::Missing)
- }
- fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
- let id = self.body.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src);
- id
- }
-
- fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
- self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
- }
- fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
- let src = self.expander.to_source(ptr);
- let id = self.make_pat(pat, src.clone());
- self.source_map.pat_map.insert(src, id);
- id
- }
- fn missing_pat(&mut self) -> PatId {
- self.body.pats.alloc(Pat::Missing)
- }
- fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
- let id = self.body.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src);
- id
- }
-
- fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
- let src = self.expander.to_source(ptr);
- let id = self.make_label(label, src.clone());
- self.source_map.label_map.insert(src, id);
- id
- }
- fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
- let id = self.body.labels.alloc(label);
- self.source_map.label_map_back.insert(id, src);
- id
+ self.expander.ctx(self.db)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -229,6 +241,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
+ // FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch());
@@ -246,18 +259,12 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
ast::Expr::LetExpr(e) => {
- let pat = self.collect_pat_opt(e.pat());
+ let pat = self.collect_pat_top(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => {
- self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
- id,
- statements,
- tail,
- })
- }
+ Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -267,50 +274,77 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
- self.collect_block_(e, |id, statements, tail| Expr::Block {
- id,
- statements,
- tail,
- label: Some(label),
+ self.with_labeled_rib(label, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Block {
+ id,
+ statements,
+ tail,
+ label: Some(label),
+ })
+ })
+ }
+ Some(ast::BlockModifier::Async(_)) => {
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Async {
+ id,
+ statements,
+ tail,
+ })
+ })
+ }
+ Some(ast::BlockModifier::Const(_)) => {
+ self.with_label_rib(RibKind::Constant, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
+ let inner_expr = this.collect_block(e);
+ let x = this.db.intern_anonymous_const(ConstBlockLoc {
+ parent: this.owner,
+ root: inner_expr,
+ });
+ this.body.exprs[result_expr_id] = Expr::Const(x);
+ this.current_binding_owner = prev_binding_owner;
+ result_expr_id
})
}
- Some(ast::BlockModifier::Async(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
- Some(ast::BlockModifier::Const(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
-
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
}
- ast::Expr::ForExpr(e) => {
- let label = e.label().map(|label| self.collect_label(label));
- let iterable = self.collect_expr_opt(e.iterable());
- let pat = self.collect_pat_opt(e.pat());
- let body = self.collect_block_opt(e.loop_body());
- self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
- }
+ ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
- let callee = self.collect_expr_opt(e.expr());
- let args = if let Some(arg_list) = e.arg_list() {
- arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
- } else {
- Box::default()
+ let is_rustc_box = {
+ let attrs = e.attrs();
+ attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
};
- self.alloc_expr(
- Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
- syntax_ptr,
- )
+ if is_rustc_box {
+ let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
+ self.alloc_expr(Expr::Box { expr }, syntax_ptr)
+ } else {
+ let callee = self.collect_expr_opt(e.expr());
+ let args = if let Some(arg_list) = e.arg_list() {
+ arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
+ } else {
+ Box::default()
+ };
+ self.alloc_expr(
+ Expr::Call {
+ callee,
+ args,
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
+ syntax_ptr,
+ )
+ }
}
ast::Expr::MethodCallExpr(e) => {
let receiver = self.collect_expr_opt(e.receiver());
@@ -336,7 +370,7 @@ impl ExprCollector<'_> {
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
- pat: self.collect_pat_opt(arm.pat()),
+ pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
@@ -357,16 +391,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
}
- ast::Expr::ContinueExpr(e) => self.alloc_expr(
- Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- ),
+ ast::Expr::ContinueExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
+ self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+ }
ast::Expr::BreakExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
let expr = e.expr().map(|e| self.collect_expr(e));
- self.alloc_expr(
- Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
}
ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr());
@@ -437,10 +475,7 @@ impl ExprCollector<'_> {
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
- ast::Expr::TryExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Try { expr }, syntax_ptr)
- }
+ ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@@ -470,14 +505,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
- ast::Expr::ClosureExpr(e) => {
+ ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
- let pat = self.collect_pat_opt(param.pat());
+ let pat = this.collect_pat_top(param.pat());
let type_ref =
- param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
args.push(pat);
arg_types.push(type_ref);
}
@@ -485,14 +522,14 @@ impl ExprCollector<'_> {
let ret_type = e
.ret_type()
.and_then(|r| r.ty())
- .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
- let prev_is_lowering_generator = self.is_lowering_generator;
- self.is_lowering_generator = false;
+ let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
+ let prev_try_block_label = this.current_try_block_label.take();
- let body = self.collect_expr_opt(e.body());
+ let body = this.collect_expr_opt(e.body());
- let closure_kind = if self.is_lowering_generator {
+ let closure_kind = if this.is_lowering_generator {
let movability = if e.static_token().is_some() {
Movability::Static
} else {
@@ -504,19 +541,21 @@ impl ExprCollector<'_> {
} else {
ClosureKind::Closure
};
- self.is_lowering_generator = prev_is_lowering_generator;
-
- self.alloc_expr(
- Expr::Closure {
- args: args.into(),
- arg_types: arg_types.into(),
- ret_type,
- body,
- closure_kind,
- },
- syntax_ptr,
- )
- }
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ this.is_lowering_generator = prev_is_lowering_generator;
+ this.current_binding_owner = prev_binding_owner;
+ this.current_try_block_label = prev_try_block_label;
+ this.body.exprs[result_expr_id] = Expr::Closure {
+ args: args.into(),
+ arg_types: arg_types.into(),
+ ret_type,
+ body,
+ closure_kind,
+ capture_by,
+ };
+ result_expr_id
+ }),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -528,9 +567,18 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
- let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ // if there is a leading comma, the user is most likely to type out a leading expression
+ // so we insert a missing expression at the beginning for IDE features
+ if comma_follows_token(e.l_paren_token()) {
+ exprs.insert(0, self.missing_expr());
+ }
+
self.alloc_expr(
- Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
+ Expr::Tuple {
+ exprs: exprs.into_boxed_slice(),
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
syntax_ptr,
)
}
@@ -555,7 +603,17 @@ impl ExprCollector<'_> {
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
- let repeat = self.collect_expr_opt(repeat);
+ let repeat = self.with_label_rib(RibKind::Constant, |this| {
+ if let Some(repeat) = repeat {
+ let syntax_ptr = AstPtr::new(&repeat);
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_expr(repeat),
+ syntax_ptr,
+ )
+ } else {
+ this.missing_expr()
+ }
+ });
self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr,
@@ -601,6 +659,240 @@ impl ExprCollector<'_> {
})
}
+ fn initialize_binding_owner(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> (ExprId, Option<ExprId>) {
+ let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+ let prev_binding_owner = self.current_binding_owner.take();
+ self.current_binding_owner = Some(result_expr_id);
+ (result_expr_id, prev_binding_owner)
+ }
+
+ /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+ /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+ /// their own body. Don't add more usage for this function so that we can remove this function after
+ /// separating those bodies.
+ fn collect_as_a_binding_owner_bad(
+ &mut self,
+ job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+ let tmp = job(self);
+ self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+ self.current_binding_owner = prev_owner;
+ id
+ }
+
+ /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+ /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
+ /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
+ fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
+ let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
+ return self.collect_block(e);
+ };
+ let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+ let old_label = self.current_try_block_label.replace(label);
+
+ let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+ let mut btail = None;
+ let block = this.collect_block_(e, |id, statements, tail| {
+ btail = tail;
+ Expr::Block { id, statements, tail, label: Some(label) }
+ });
+ (btail, block)
+ });
+
+ let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
+ let next_tail = match btail {
+ Some(tail) => self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([tail]),
+ is_assignee_expr: false,
+ }),
+ None => {
+ let unit = self.alloc_expr_desugared(Expr::Tuple {
+ exprs: Box::new([]),
+ is_assignee_expr: false,
+ });
+ self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([unit]),
+ is_assignee_expr: false,
+ })
+ }
+ };
+ let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+ unreachable!("block was lowered to non-block");
+ };
+ *tail = Some(next_tail);
+ self.current_try_block_label = old_label;
+ expr_id
+ }
+
+ /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
+ /// ```ignore (pseudo-rust)
+ /// match IntoIterator::into_iter(<head>) {
+ /// mut iter => {
+ /// [opt_ident]: loop {
+ /// match Iterator::next(&mut iter) {
+ /// None => break,
+ /// Some(<pat>) => <body>,
+ /// };
+ /// }
+ /// }
+ /// }
+ /// ```
+ fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
+ let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| {
+ Some((
+ LangItem::IntoIterIntoIter.path(self.db, self.krate)?,
+ LangItem::IteratorNext.path(self.db, self.krate)?,
+ LangItem::OptionSome.path(self.db, self.krate)?,
+ LangItem::OptionNone.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let head = self.collect_expr_opt(e.iterable());
+ let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone());
+ let iterator = self.alloc_expr(
+ Expr::Call {
+ callee: into_iter_fn_expr,
+ args: Box::new([head]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let none_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+ guard: None,
+ expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()),
+ };
+ let some_pat = Pat::TupleStruct {
+ path: Some(Box::new(option_some)),
+ args: Box::new([self.collect_pat_top(e.pat())]),
+ ellipsis: None,
+ };
+ let label = e.label().map(|label| self.collect_label(label));
+ let some_arm = MatchArm {
+ pat: self.alloc_pat_desugared(some_pat),
+ guard: None,
+ expr: self.with_opt_labeled_rib(label, |this| {
+ this.collect_expr_opt(e.loop_body().map(|x| x.into()))
+ }),
+ };
+ let iter_name = Name::generate_new_name();
+ let iter_expr =
+ self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr.clone());
+ let iter_expr_mut = self.alloc_expr(
+ Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
+ syntax_ptr.clone(),
+ );
+ let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone());
+ let iter_next_expr = self.alloc_expr(
+ Expr::Call {
+ callee: iter_next_fn_expr,
+ args: Box::new([iter_expr_mut]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let loop_inner = self.alloc_expr(
+ Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
+ syntax_ptr.clone(),
+ );
+ let loop_outer =
+ self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+ let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
+ let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+ self.add_definition_to_binding(iter_binding, iter_pat);
+ self.alloc_expr(
+ Expr::Match {
+ expr: iterator,
+ arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]),
+ },
+ syntax_ptr.clone(),
+ )
+ }
+
+ /// Desugar `ast::TryExpr` from: `<expr>?` into:
+ /// ```ignore (pseudo-rust)
+ /// match Try::branch(<expr>) {
+ /// ControlFlow::Continue(val) => val,
+ /// ControlFlow::Break(residual) =>
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target Try::from_residual(residual),
+ /// // Otherwise:
+ /// return Try::from_residual(residual),
+ /// }
+ /// ```
+ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
+ let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| {
+ Some((
+ LangItem::TryTraitBranch.path(self.db, self.krate)?,
+ LangItem::ControlFlowContinue.path(self.db, self.krate)?,
+ LangItem::ControlFlowBreak.path(self.db, self.krate)?,
+ LangItem::TryTraitFromResidual.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let operand = self.collect_expr_opt(e.expr());
+ let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
+ let expr = self.alloc_expr(
+ Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
+ syntax_ptr.clone(),
+ );
+ let continue_name = Name::generate_new_name();
+ let continue_binding =
+ self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
+ let continue_bpat =
+ self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
+ self.add_definition_to_binding(continue_binding, continue_bpat);
+ let continue_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_continue)),
+ args: Box::new([continue_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
+ };
+ let break_name = Name::generate_new_name();
+ let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
+ let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
+ self.add_definition_to_binding(break_binding, break_bpat);
+ let break_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_break)),
+ args: Box::new([break_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: {
+ let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
+ let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
+ let result = self.alloc_expr(
+ Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
+ syntax_ptr.clone(),
+ );
+ self.alloc_expr(
+ match self.current_try_block_label {
+ Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+ None => Expr::Return { expr: Some(result) },
+ },
+ syntax_ptr.clone(),
+ )
+ },
+ };
+ let arms = Box::new([continue_arm, break_arm]);
+ self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
+ }
+
fn collect_macro_call<F, T, U>(
&mut self,
mcall: ast::MacroCall,
@@ -616,7 +908,19 @@ impl ExprCollector<'_> {
let outer_file = self.expander.current_file_id;
let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall));
- let res = self.expander.enter_expand(self.db, mcall);
+ let module = self.expander.module.local_id;
+ let res = self.expander.enter_expand(self.db, mcall, |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ });
let res = match res {
Ok(res) => res,
@@ -639,7 +943,7 @@ impl ExprCollector<'_> {
krate: *krate,
});
}
- Some(ExpandError::RecursionOverflowPosioned) => {
+ Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication.
}
@@ -663,7 +967,11 @@ impl ExprCollector<'_> {
self.db.ast_id_map(self.expander.current_file_id),
);
- let id = collector(self, Some(expansion));
+ if record_diagnostics {
+ // FIXME: Report parse errors here
+ }
+
+ let id = collector(self, Some(expansion.tree()));
self.ast_id_map = prev_ast_id_map;
self.expander.exit(self.db, mark);
id
@@ -720,7 +1028,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() {
return;
}
- let pat = self.collect_pat_opt(stmt.pat());
+ let pat = self.collect_pat_top(stmt.pat());
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -763,22 +1071,36 @@ impl ExprCollector<'_> {
fn collect_block_(
&mut self,
block: ast::BlockExpr,
- mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+ mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
- let file_local_id = self.ast_id_map.ast_id(&block);
- let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
- let block_loc =
- BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
- let block_id = self.db.intern_block(block_loc);
-
- let (module, def_map) = match self.db.block_def_map(block_id) {
- Some(def_map) => {
- self.body.block_scopes.push(block_id);
- (def_map.root(), def_map)
- }
- None => (self.expander.module, self.expander.def_map.clone()),
+ let block_has_items = {
+ let statement_has_item = block.statements().any(|stmt| match stmt {
+ ast::Stmt::Item(_) => true,
+ // Macro calls can be both items and expressions. The syntax library always treats
+ // them as expressions here, so we undo that.
+ ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+ _ => false,
+ });
+ statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
};
- let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
+
+ let block_id = if block_has_items {
+ let file_local_id = self.ast_id_map.ast_id(&block);
+ let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+ Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module }))
+ } else {
+ None
+ };
+
+ let (module, def_map) =
+ match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
+ Some((def_map, block_id)) => {
+ self.body.block_scopes.push(block_id);
+ (def_map.module_id(DefMap::ROOT), def_map)
+ }
+ None => (self.expander.module, self.def_map.clone()),
+ };
+ let prev_def_map = mem::replace(&mut self.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);
let mut statements = Vec::new();
@@ -800,7 +1122,7 @@ impl ExprCollector<'_> {
let expr_id = self
.alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
- self.expander.def_map = prev_def_map;
+ self.def_map = prev_def_map;
self.expander.module = prev_local_module;
expr_id
}
@@ -812,43 +1134,46 @@ impl ExprCollector<'_> {
}
}
- fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
- let label = Label {
- name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
- };
- self.alloc_label(label, AstPtr::new(&ast_label))
+ fn collect_labelled_block_opt(
+ &mut self,
+ label: Option<LabelId>,
+ expr: Option<ast::BlockExpr>,
+ ) -> ExprId {
+ match label {
+ Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+ None => self.collect_block_opt(expr),
+ }
}
- fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
- self.collect_pat_(pat, &mut BindingList::default())
- }
+ // region: patterns
- fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+ fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat {
- Some(pat) => self.collect_pat(pat),
+ Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(),
}
}
- fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
- let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+ let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none();
let (binding, pattern) = if is_simple_ident_pat {
// This could also be a single-segment path pattern. To
// decide that, we need to try resolving the name.
- let (resolved, _) = self.expander.def_map.resolve_path(
+ let (resolved, _) = self.def_map.resolve_path(
self.db,
- self.expander.module,
+ self.expander.module.local_id,
&name.clone().into(),
BuiltinShadowMode::Other,
+ None,
);
match resolved.take_values() {
Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
@@ -887,11 +1212,15 @@ impl ExprCollector<'_> {
ast::Pat::TupleStructPat(p) => {
let path =
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
- let pat = self.collect_pat_opt_(p.pat(), binding_list);
+ let pat = self.collect_pat_opt(p.pat(), binding_list);
let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability }
}
@@ -900,13 +1229,42 @@ impl ExprCollector<'_> {
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
- ast::Pat::OrPat(p) => {
- let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
- Pat::Or(pats)
+ ast::Pat::OrPat(p) => 'b: {
+ let prev_is_used = mem::take(&mut binding_list.is_used);
+ let prev_reject_new = mem::take(&mut binding_list.reject_new);
+ let mut pats = Vec::with_capacity(p.pats().count());
+ let mut it = p.pats();
+ let Some(first) = it.next() else {
+ break 'b Pat::Or(Box::new([]));
+ };
+ pats.push(self.collect_pat(first, binding_list));
+ binding_list.reject_new = true;
+ for rest in it {
+ for (_, x) in binding_list.is_used.iter_mut() {
+ *x = false;
+ }
+ pats.push(self.collect_pat(rest, binding_list));
+ for (&id, &x) in binding_list.is_used.iter() {
+ if !x {
+ self.body.bindings[id].problems =
+ Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ }
+ binding_list.reject_new = prev_reject_new;
+ let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used);
+ for (id, _) in current_is_used.into_iter() {
+ binding_list.check_is_used(self, id);
+ }
+ Pat::Or(pats.into())
}
- ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+ ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
ast::Pat::TuplePat(p) => {
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::Tuple { args, ellipsis }
}
ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -919,7 +1277,7 @@ impl ExprCollector<'_> {
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
- let pat = self.collect_pat_(ast_pat, binding_list);
+ let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
@@ -938,26 +1296,18 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat`
Pat::Slice {
- prefix: prefix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
- slice: slice.map(|p| self.collect_pat_(p, binding_list)),
- suffix: suffix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
+ prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+ slice: slice.map(|p| self.collect_pat(p, binding_list)),
+ suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
}
}
- ast::Pat::LiteralPat(lit) => {
- if let Some(ast_lit) = lit.literal() {
- let expr = Expr::Literal(ast_lit.kind().into());
- let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
- let expr_id = self.alloc_expr(expr, expr_ptr);
- Pat::Lit(expr_id)
- } else {
- Pat::Missing
- }
+ #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
+ ast::Pat::LiteralPat(lit) => 'b: {
+ let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
+ let expr = Expr::Literal(hir_lit);
+ let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
+ let expr_id = self.alloc_expr(expr, expr_ptr);
+ Pat::Lit(expr_id)
}
ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped
@@ -969,12 +1319,18 @@ impl ExprCollector<'_> {
Pat::Missing
}
ast::Pat::BoxPat(boxpat) => {
- let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+ let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
ast::Pat::ConstBlockPat(const_block_pat) => {
- if let Some(expr) = const_block_pat.block_expr() {
- let expr_id = self.collect_block(expr);
+ if let Some(block) = const_block_pat.block_expr() {
+ let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+ let syntax_ptr = AstPtr::new(&block.clone().into());
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_block(block),
+ syntax_ptr,
+ )
+ });
Pat::ConstBlock(expr_id)
} else {
Pat::Missing
@@ -986,23 +1342,45 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
- this.collect_pat_opt_(expanded_pat, binding_list)
+ this.collect_pat_opt(expanded_pat, binding_list)
});
self.source_map.pat_map.insert(src, pat);
return pat;
}
None => Pat::Missing,
},
- // FIXME: implement
- ast::Pat::RangePat(_) => Pat::Missing,
+ // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
+ ast::Pat::RangePat(p) => {
+ let mut range_part_lower = |p: Option<ast::Pat>| {
+ p.and_then(|x| match &x {
+ ast::Pat::LiteralPat(x) => {
+ Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0)))
+ }
+ ast::Pat::IdentPat(p) => {
+ let name =
+ p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+ Some(Box::new(LiteralOrConst::Const(name.into())))
+ }
+ ast::Pat::PathPat(p) => p
+ .path()
+ .and_then(|path| self.expander.parse_path(self.db, path))
+ .map(LiteralOrConst::Const)
+ .map(Box::new),
+ _ => None,
+ })
+ };
+ let start = range_part_lower(p.start());
+ let end = range_part_lower(p.end());
+ Pat::Range { start, end }
+ }
};
let ptr = AstPtr::new(&pat);
self.alloc_pat(pattern, Either::Left(ptr))
}
- fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
match pat {
- Some(pat) => self.collect_pat_(pat, binding_list),
+ Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(),
}
}
@@ -1010,20 +1388,28 @@ impl ExprCollector<'_> {
fn collect_tuple_pat(
&mut self,
args: AstChildren<ast::Pat>,
+ has_leading_comma: bool,
binding_list: &mut BindingList,
) -> (Box<[PatId]>, Option<usize>) {
// Find the location of the `..`, if there is one. Note that we do not
// consider the possibility of there being multiple `..` here.
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
// We want to skip the `..` pattern here, since we account for it above.
- let args = args
+ let mut args: Vec<_> = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
- .map(|p| self.collect_pat_(p, binding_list))
+ .map(|p| self.collect_pat(p, binding_list))
.collect();
+ // if there is a leading comma, the user is most likely to type out a leading pattern
+ // so we insert a missing pattern at the beginning for IDE features
+ if has_leading_comma {
+ args.insert(0, self.missing_pat());
+ }
- (args, ellipsis)
+ (args.into_boxed_slice(), ellipsis)
}
+ // endregion: patterns
+
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1051,42 +1437,150 @@ impl ExprCollector<'_> {
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id);
}
-}
-impl From<ast::LiteralKind> for Literal {
- fn from(ast_lit_kind: ast::LiteralKind) -> Self {
- match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
- LiteralKind::IntNumber(lit) => {
- if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
- Literal::Float(
- FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
- builtin,
- )
- } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
- Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
- } else {
- let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
- Literal::Uint(lit.value().unwrap_or(0), builtin)
+ // region: labels
+
+ fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+ let label = Label {
+ name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+ };
+ self.alloc_label(label, AstPtr::new(&ast_label))
+ }
+
+ fn resolve_label(
+ &self,
+ lifetime: Option<ast::Lifetime>,
+ ) -> Result<Option<LabelId>, BodyDiagnostic> {
+ let Some(lifetime) = lifetime else {
+ return Ok(None)
+ };
+ let name = Name::new_lifetime(&lifetime);
+
+ for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
+ if let Some((label_name, id)) = &rib.label {
+ if *label_name == name {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(BodyDiagnostic::UnreachableLabel {
+ name,
+ node: InFile::new(
+ self.expander.current_file_id,
+ AstPtr::new(&lifetime),
+ ),
+ })
+ };
}
}
- LiteralKind::FloatNumber(lit) => {
- let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
- Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
- }
- LiteralKind::ByteString(bs) => {
- let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::ByteString(text)
- }
- LiteralKind::String(s) => {
- let text = s.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::String(text)
- }
- LiteralKind::Byte(b) => {
- Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
- }
- LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
- LiteralKind::Bool(val) => Literal::Bool(val),
}
+
+ Err(BodyDiagnostic::UndeclaredLabel {
+ name,
+ node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+ })
+ }
+
+ fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+ !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+ }
+
+ fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new(kind));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_opt_labeled_rib<T>(
+ &mut self,
+ label: Option<LabelId>,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
+ match label {
+ None => f(self),
+ Some(label) => self.with_labeled_rib(label, f),
+ }
+ }
+ // endregion: labels
+}
+
+fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
+ let ast_lit = lit.literal()?;
+ let mut hir_lit: Literal = ast_lit.kind().into();
+ if lit.minus_token().is_some() {
+ let Some(h) = hir_lit.negate() else {
+ return None;
+ };
+ hir_lit = h;
+ }
+ Some((hir_lit, ast_lit))
+}
+
+impl ExprCollector<'_> {
+ fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.exprs.alloc(expr);
+ self.source_map.expr_map_back.insert(id, src.clone());
+ self.source_map.expr_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+ self.body.exprs.alloc(expr)
+ }
+ fn missing_expr(&mut self) -> ExprId {
+ self.alloc_expr_desugared(Expr::Missing)
+ }
+
+ fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+ let binding = self.body.bindings.alloc(Binding {
+ name,
+ mode,
+ definitions: SmallVec::new(),
+ problems: None,
+ });
+ if let Some(owner) = self.current_binding_owner {
+ self.body.binding_owners.insert(binding, owner);
+ }
+ binding
+ }
+
+ fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.pats.alloc(pat);
+ self.source_map.pat_map_back.insert(id, src.clone());
+ self.source_map.pat_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+ self.body.pats.alloc(pat)
+ }
+ fn missing_pat(&mut self) -> PatId {
+ self.body.pats.alloc(Pat::Missing)
+ }
+
+ fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.labels.alloc(label);
+ self.source_map.label_map_back.insert(id, src.clone());
+ self.source_map.label_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+ self.body.labels.alloc(label)
}
}
+
+fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
+ (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
+ .map_or(false, |it| it.kind() == syntax::T![,])
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5a9b825a2..cd6df0e63 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,10 +2,14 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
use syntax::ast::HasName;
use crate::{
- expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
+ hir::{
+ Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
+ Movability, Statement,
+ },
pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef,
};
@@ -13,47 +17,71 @@ use crate::{
use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
- let needs_semi;
let header = match owner {
DefWithBodyId::FunctionId(it) => {
- needs_semi = false;
let item_tree_id = it.lookup(db).id;
- format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "fn {}",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::StaticId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
- format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "static {} = ",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::ConstId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
- Some(name) => name.to_string(),
+ Some(name) => name.display(db.upcast()).to_string(),
None => "_".to_string(),
};
format!("const {name} = ")
}
+ DefWithBodyId::InTypeConstId(_) => format!("In type const = "),
DefWithBodyId::VariantId(it) => {
- needs_semi = false;
let src = it.parent.child_source(db);
let variant = &src.value[it.local_id];
- let name = match &variant.name() {
+ match &variant.name() {
Some(name) => name.to_string(),
None => "_".to_string(),
- };
- format!("{name}")
+ }
}
};
- let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p =
+ Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ if let DefWithBodyId::FunctionId(it) = owner {
+ p.buf.push('(');
+ body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
+ p.print_pat(param);
+ p.buf.push(':');
+ p.print_type_ref(ty);
+ });
+ p.buf.push(')');
+ p.buf.push(' ');
+ }
p.print_expr(body.body_expr);
- if needs_semi {
+ if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';');
}
p.buf
}
+pub(super) fn print_expr_hir(
+ db: &dyn DefDatabase,
+ body: &Body,
+ _owner: DefWithBodyId,
+ expr: ExprId,
+) -> String {
+ let mut p =
+ Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ p.print_expr(expr);
+ p.buf
+}
+
macro_rules! w {
($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); }
@@ -70,6 +98,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -144,29 +173,19 @@ impl<'a> Printer<'a> {
}
Expr::Loop { body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "loop ");
self.print_expr(*body);
}
Expr::While { condition, body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "while ");
self.print_expr(*condition);
self.print_expr(*body);
}
- Expr::For { iterable, pat, body, label } => {
- if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
- }
- w!(self, "for ");
- self.print_pat(*pat);
- w!(self, " in ");
- self.print_expr(*iterable);
- self.print_expr(*body);
- }
Expr::Call { callee, args, is_assignee_expr: _ } => {
self.print_expr(*callee);
w!(self, "(");
@@ -182,10 +201,10 @@ impl<'a> Printer<'a> {
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver);
- w!(self, ".{}", method_name);
+ w!(self, ".{}", method_name.display(self.db));
if let Some(args) = generic_args {
w!(self, "::<");
- print_generic_args(args, self).unwrap();
+ print_generic_args(self.db, args, self).unwrap();
w!(self, ">");
}
w!(self, "(");
@@ -219,14 +238,14 @@ impl<'a> Printer<'a> {
}
Expr::Continue { label } => {
w!(self, "continue");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
}
Expr::Break { expr, label } => {
w!(self, "break");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
if let Some(expr) = expr {
self.whitespace();
@@ -265,7 +284,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name);
+ w!(p, "{}: ", field.name.display(self.db));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -282,16 +301,12 @@ impl<'a> Printer<'a> {
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name);
+ w!(self, ".{}", name.display(self.db));
}
Expr::Await { expr } => {
self.print_expr(*expr);
w!(self, ".await");
}
- Expr::Try { expr } => {
- self.print_expr(*expr);
- w!(self, "?");
- }
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
@@ -359,7 +374,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index);
w!(self, "]");
}
- Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+ Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
match closure_kind {
ClosureKind::Generator(Movability::Static) => {
w!(self, "static ");
@@ -369,6 +384,12 @@ impl<'a> Printer<'a> {
}
_ => (),
}
+ match capture_by {
+ CaptureBy::Value => {
+ w!(self, "move ");
+ }
+ CaptureBy::Ref => (),
+ }
w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 {
@@ -418,20 +439,17 @@ impl<'a> Printer<'a> {
}
Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => {
- let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+ let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
self.print_block(label.as_deref(), statements, tail);
}
Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), statements, tail);
}
- Expr::TryBlock { id: _, statements, tail } => {
- self.print_block(Some("try "), statements, tail);
- }
Expr::Async { id: _, statements, tail } => {
self.print_block(Some("async "), statements, tail);
}
- Expr::Const { id: _, statements, tail } => {
- self.print_block(Some("const "), statements, tail);
+ Expr::Const(id) => {
+ w!(self, "const {{ /* {id:?} */ }}");
}
}
}
@@ -439,7 +457,7 @@ impl<'a> Printer<'a> {
fn print_block(
&mut self,
label: Option<&str>,
- statements: &Box<[Statement]>,
+ statements: &[Statement],
tail: &Option<la_arena::Idx<Expr>>,
) {
self.whitespace();
@@ -449,7 +467,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
if !statements.is_empty() || tail.is_some() {
self.indented(|p| {
- for stmt in &**statements {
+ for stmt in statements {
p.print_stmt(stmt);
}
if let Some(tail) = tail {
@@ -497,7 +515,7 @@ impl<'a> Printer<'a> {
w!(self, " {{");
self.indented(|p| {
for arg in args.iter() {
- w!(p, "{}: ", arg.name);
+ w!(p, "{}: ", arg.name.display(self.db));
p.print_pat(arg.pat);
wln!(p, ",");
}
@@ -508,9 +526,13 @@ impl<'a> Printer<'a> {
w!(self, "}}");
}
Pat::Range { start, end } => {
- self.print_expr(*start);
- w!(self, "...");
- self.print_expr(*end);
+ if let Some(start) = start {
+ self.print_literal_or_const(start);
+ }
+ w!(self, "..=");
+ if let Some(end) = end {
+ self.print_literal_or_const(end);
+ }
}
Pat::Slice { prefix, slice, suffix } => {
w!(self, "[");
@@ -601,10 +623,18 @@ impl<'a> Printer<'a> {
}
}
+ fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+ match literal_or_const {
+ LiteralOrConst::Literal(l) => self.print_literal(l),
+ LiteralOrConst::Const(c) => self.print_path(c),
+ }
+ }
+
fn print_literal(&mut self, literal: &Literal) {
match literal {
Literal::String(it) => w!(self, "{:?}", it),
Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
+ Literal::CString(it) => w!(self, "\"{}\\0\"", it),
Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
Literal::Bool(it) => w!(self, "{}", it),
Literal::Int(i, suffix) => {
@@ -629,11 +659,11 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, ty: &TypeRef) {
- print_type_ref(ty, self).unwrap();
+ print_type_ref(self.db, ty, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_binding(&mut self, id: BindingId) {
@@ -644,6 +674,6 @@ impl<'a> Printer<'a> {
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name);
+ w!(self, "{}{}", mode, name.display(self.db));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 12fc1f116..69741c445 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -1,14 +1,13 @@
//! Name resolution for expressions.
-use std::sync::Arc;
-
use hir_expand::name::Name;
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, IdxRange, RawIdx};
use rustc_hash::FxHashMap;
+use triomphe::Arc;
use crate::{
body::Body,
db::DefDatabase,
- expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+ hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId,
};
@@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
scopes: Arena<ScopeData>,
+ scope_entries: Arena<ScopeEntry>,
scope_by_expr: FxHashMap<ExprId, ScopeId>,
}
@@ -41,7 +41,7 @@ pub struct ScopeData {
parent: Option<ScopeId>,
block: Option<BlockId>,
label: Option<(LabelId, Name)>,
- entries: Vec<ScopeEntry>,
+ entries: IdxRange<ScopeEntry>,
}
impl ExprScopes {
@@ -53,7 +53,7 @@ impl ExprScopes {
}
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
- &self.scopes[scope].entries
+ &self.scope_entries[self.scopes[scope].entries.clone()]
}
/// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
@@ -85,10 +85,17 @@ impl ExprScopes {
}
}
+fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
+ IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32)))
+}
+
impl ExprScopes {
fn new(body: &Body) -> ExprScopes {
- let mut scopes =
- ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
+ let mut scopes = ExprScopes {
+ scopes: Arena::default(),
+ scope_entries: Arena::default(),
+ scope_by_expr: FxHashMap::default(),
+ };
let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
@@ -96,7 +103,12 @@ impl ExprScopes {
}
fn root_scope(&mut self) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: None,
+ block: None,
+ label: None,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
@@ -104,32 +116,38 @@ impl ExprScopes {
parent: Some(parent),
block: None,
label: None,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: Some(parent),
+ block: None,
+ label,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_block_scope(
&mut self,
parent: ScopeId,
- block: BlockId,
+ block: Option<BlockId>,
label: Option<(LabelId, Name)>,
) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: Some(parent),
- block: Some(block),
+ block,
label,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
let Binding { name, .. } = &body.bindings[binding];
- let entry = ScopeEntry { name: name.clone(), binding };
- self.scopes[scope].entries.push(entry);
+ let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
+ self.scopes[scope].entries =
+ IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
}
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@@ -150,9 +168,9 @@ impl ExprScopes {
}
fn shrink_to_fit(&mut self) {
- let ExprScopes { scopes, scope_by_expr } = self;
+ let ExprScopes { scopes, scope_entries, scope_by_expr } = self;
scopes.shrink_to_fit();
- scopes.values_mut().for_each(|it| it.entries.shrink_to_fit());
+ scope_entries.shrink_to_fit();
scope_by_expr.shrink_to_fit();
}
}
@@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::Unsafe { id, statements, tail }
- | Expr::Async { id, statements, tail }
- | Expr::Const { id, statements, tail }
- | Expr::TryBlock { id, statements, tail } => {
+ Expr::Const(_) => {
+ // FIXME: This is broken.
+ }
+ Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
let mut scope = scopes.new_block_scope(*scope, *id, None);
// Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::For { iterable, pat, body: body_expr, label } => {
- compute_expr_scopes(*iterable, body, scopes, scope);
- let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
- scopes.add_pat_bindings(body, scope, *pat);
- compute_expr_scopes(*body_expr, body, scopes, &mut scope);
- }
Expr::While { condition, body: body_expr, label } => {
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*condition, body, scopes, &mut scope);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 77ac221e5..6e77744f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -148,8 +148,8 @@ fn f() {
}
"#,
expect![[r#"
- BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
- BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
+ BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
+ BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
crate scope
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
index dd69c3ab4..61b248197 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
@@ -106,8 +106,14 @@ impl AsName for BuiltinType {
impl fmt::Display for BuiltinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let type_name = self.as_name();
- type_name.fmt(f)
+ match self {
+ BuiltinType::Char => f.write_str("char"),
+ BuiltinType::Bool => f.write_str("bool"),
+ BuiltinType::Str => f.write_str("str"),
+ BuiltinType::Int(it) => it.fmt(f),
+ BuiltinType::Uint(it) => it.fmt(f),
+ BuiltinType::Float(it) => it.fmt(f),
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 68b57acca..bb79e28f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
use crate::{
db::DefDatabase,
- dyn_map::DynMap,
+ dyn_map::{keys, DynMap},
item_scope::ItemScope,
- keys,
+ nameres::DefMap,
src::{HasChildSource, HasSource},
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
ModuleDefId, ModuleId, TraitId, VariantId,
@@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
for (_, def_map) in body.blocks(db) {
// All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`.
- def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
+ def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 1633a33be..40e6a4308 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -1,22 +1,28 @@
//! Contains basic data about various HIR declarations.
-use std::sync::Arc;
+pub mod adt;
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
+use hir_expand::{
+ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
+};
use intern::Interned;
use smallvec::SmallVec;
-use syntax::ast;
+use syntax::{ast, Parse};
+use triomphe::Arc;
use crate::{
attr::Attrs,
- body::{Expander, Mark},
db::DefDatabase,
- item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
+ expander::{Expander, Mark},
+ item_tree::{
+ self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId,
+ },
+ macro_call_as_call_id, macro_id_to_def_id,
nameres::{
attr_resolution::ResolvedAttr,
diagnostics::DefDiagnostic,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
- DefMap,
+ DefMap, MacroSubNs,
},
type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
@@ -28,9 +34,8 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData {
pub name: Name,
- pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
+ pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub attrs: Attrs,
pub visibility: RawVisibility,
pub abi: Option<Interned<str>>,
@@ -43,16 +48,16 @@ impl FunctionData {
pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
let loc = func.lookup(db);
let krate = loc.container.module(db).krate;
- let crate_graph = db.crate_graph();
- let cfg_options = &crate_graph[krate].cfg_options;
let item_tree = loc.id.item_tree(db);
let func = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[func.visibility].clone()
};
+ let crate_graph = db.crate_graph();
+ let cfg_options = &crate_graph[krate].cfg_options;
let enabled_params = func
.params
.clone()
@@ -99,12 +104,11 @@ impl FunctionData {
params: enabled_params
.clone()
.filter_map(|id| match &item_tree[id] {
- Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
+ Param::Normal(ty) => Some(ty.clone()),
Param::Varargs => None,
})
.collect(),
ret_type: func.ret_type.clone(),
- async_ret_type: func.async_ret_type.clone(),
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
visibility,
abi: func.abi.clone(),
@@ -188,7 +192,7 @@ impl TypeAliasData {
let item_tree = loc.id.item_tree(db);
let typ = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[typ.visibility].clone()
};
@@ -471,7 +475,7 @@ impl ConstData {
let item_tree = loc.id.item_tree(db);
let konst = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[konst.visibility].clone()
};
@@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
db: &'a dyn DefDatabase,
module_id: ModuleId,
def_map: Arc<DefMap>,
- inactive_diagnostics: Vec<DefDiagnostic>,
+ diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId,
expander: Expander,
@@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
expander: Expander::new(db, file_id, module_id),
items: Vec::new(),
attr_calls: Vec::new(),
- inactive_diagnostics: Vec::new(),
+ diagnostics: Vec::new(),
}
}
@@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
(
self.items,
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
- self.inactive_diagnostics,
+ self.diagnostics,
)
}
- // FIXME: proc-macro diagnostics
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
let container = self.container;
self.items.reserve(assoc_items.len());
@@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
'items: for &item in assoc_items {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
- self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id,
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
attrs.cfg().unwrap(),
@@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
- if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
+ match self.def_map.resolve_attr_macro(
self.db,
self.module_id.local_id,
ast_id_with_path,
attr,
) {
- self.attr_calls.push((ast_id, call_id));
- // If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
- continue 'attrs;
- }
- let loc = self.db.lookup_intern_macro_call(call_id);
- if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
- // If there's no expander for the proc macro (e.g. the
- // proc macro is ignored, or building the proc macro
- // crate failed), skip expansion like we would if it was
- // disabled. This is analogous to the handling in
- // `DefCollector::collect_macros`.
- if exp.is_dummy() {
+ Ok(ResolvedAttr::Macro(call_id)) => {
+ self.attr_calls.push((ast_id, call_id));
+ // If proc attribute macro expansion is disabled, skip expanding it here
+ if !self.db.expand_proc_attr_macros() {
continue 'attrs;
}
- }
- match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
- ExpandResult { value: Some((mark, _)), .. } => {
- self.collect_macro_items(mark);
- continue 'items;
+ let loc = self.db.lookup_intern_macro_call(call_id);
+ if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
+ // If there's no expander for the proc macro (e.g. the
+ // proc macro is ignored, or building the proc macro
+ // crate failed), skip expansion like we would if it was
+ // disabled. This is analogous to the handling in
+ // `DefCollector::collect_macros`.
+ if exp.is_dummy() {
+ continue 'attrs;
+ }
}
- ExpandResult { .. } => {}
+
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| loc.kind.clone());
+ continue 'items;
+ }
+ Ok(_) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::Attr {
+ ast_id,
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
+ invoc_attr_index: attr.id,
+ },
+ attr.path().clone(),
+ ));
}
}
}
- match item {
- AssocItem::Function(id) => {
- let item = &item_tree[id];
+ self.collect_item(item_tree, tree_id, container, item);
+ }
+ }
- let def =
- FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::Const(id) => {
- let item = &item_tree[id];
-
- let name = match item.name.clone() {
- Some(name) => name,
- None => continue,
- };
- let def =
- ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((name, def.into()));
- }
- AssocItem::TypeAlias(id) => {
- let item = &item_tree[id];
+ fn collect_item(
+ &mut self,
+ item_tree: &ItemTree,
+ tree_id: TreeId,
+ container: ItemContainerId,
+ item: AssocItem,
+ ) {
+ match item {
+ AssocItem::Function(id) => {
+ let item = &item_tree[id];
- let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }
- .intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::MacroCall(call) => {
- if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) {
- let call = &item_tree[call];
-
- let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
- let call = ast_id_map.get(call.ast_id).to_node(&root);
- let _cx =
- stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
- let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
-
- if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
- self.collect_macro_items(mark);
- }
+ let def =
+ FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::Const(id) => {
+ let item = &item_tree[id];
+ let Some(name) = item.name.clone() else { return };
+ let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((name, def.into()));
+ }
+ AssocItem::TypeAlias(id) => {
+ let item = &item_tree[id];
+
+ let def =
+ TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::MacroCall(call) => {
+ let file_id = self.expander.current_file_id();
+ let MacroCall { ast_id, expand_to, ref path } = item_tree[call];
+ let module = self.expander.module.local_id;
+
+ let resolver = |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ .map(|it| macro_id_to_def_id(self.db, it))
+ };
+ match macro_call_as_call_id(
+ self.db.upcast(),
+ &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
+ expand_to,
+ self.expander.module.krate(),
+ resolver,
+ ) {
+ Ok(Some(call_id)) => {
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to: hir_expand::ExpandTo::Items,
+ });
+ }
+ Ok(None) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to,
+ },
+ Clone::clone(path),
+ ));
}
}
}
}
}
- fn collect_macro_items(&mut self, mark: Mark) {
+ fn collect_macro_items(
+ &mut self,
+ ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
+ error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
+ ) {
+ let Some((mark, parse)) = value else { return };
+
+ if let Some(err) = err {
+ let diag = match err {
+ // why is this reported here?
+ hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
+ DefDiagnostic::unresolved_proc_macro(
+ self.module_id.local_id,
+ error_call_kind(),
+ krate,
+ )
+ }
+ _ => DefDiagnostic::macro_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ err.to_string(),
+ ),
+ };
+ self.diagnostics.push(diag);
+ }
+ if let errors @ [_, ..] = parse.errors() {
+ self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ errors.into(),
+ ));
+ }
+
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
let item_tree = tree_id.item_tree(self.db);
let iter: SmallVec<[_; 2]> =
@@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
self.expander.exit(self.db, mark);
}
}
+
+fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
+ let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
+ let item_tree = tree_id.item_tree(db);
+ let tr_def = &item_tree[tree_id.value];
+ item_tree[tr_def.visibility].clone()
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index b336f59ff..6db5abccc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -1,8 +1,7 @@
//! Defines hir-level representation of structs, enums and unions
-use std::sync::Arc;
-
use base_db::CrateId;
+use bitflags::bitflags;
use cfg::CfgOptions;
use either::Either;
@@ -12,15 +11,17 @@ use hir_expand::{
};
use intern::Interned;
use la_arena::{Arena, ArenaMap};
-use rustc_abi::{Integer, IntegerType};
+use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
use syntax::ast::{self, HasName, HasVisibility};
+use triomphe::Arc;
use crate::{
- body::{CfgExpander, LowerCtx},
builtin_type::{BuiltinInt, BuiltinUint},
db::DefDatabase,
+ expander::CfgExpander,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
- layout::{Align, ReprFlags, ReprOptions},
+ lang_item::LangItem,
+ lower::LowerCtx,
nameres::diagnostics::DefDiagnostic,
src::HasChildSource,
src::HasSource,
@@ -39,8 +40,27 @@ pub struct StructData {
pub variant_data: Arc<VariantData>,
pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
- pub rustc_has_incoherent_inherent_impls: bool,
- pub fundamental: bool,
+ pub flags: StructFlags,
+}
+
+bitflags! {
+#[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct StructFlags: u8 {
+ const NO_FLAGS = 0;
+ /// Indicates whether the struct is `PhantomData`.
+ const IS_PHANTOM_DATA = 1 << 2;
+ /// Indicates whether the struct has a `#[fundamental]` attribute.
+ const IS_FUNDAMENTAL = 1 << 3;
+ // FIXME: should this be a flag?
+ /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
+ const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4;
+ /// Indicates whether this struct is `Box`.
+ const IS_BOX = 1 << 5;
+ /// Indicates whether this struct is `ManuallyDrop`.
+ const IS_MANUALLY_DROP = 1 << 6;
+ /// Indicates whether this struct is `UnsafeCell`.
+ const IS_UNSAFE_CELL = 1 << 7;
+ }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -174,10 +194,25 @@ impl StructData {
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
+ if let Some(lang) = attrs.lang_item() {
+ match lang {
+ LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
+ LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
+ LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
+ LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
+ _ => (),
+ }
+ }
let strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -196,8 +231,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[strukt.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -218,9 +252,13 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
let union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -239,8 +277,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[union.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -436,7 +473,7 @@ fn lower_struct(
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
ast: &InFile<ast::StructKind>,
) -> StructKind {
- let ctx = LowerCtx::new(db, ast.file_id);
+ let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
match &ast.value {
ast::StructKind::Tuple(fl) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 9371fc14d..04ec47f84 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,18 +1,17 @@
//! Defines database & queries for name resolution.
-use std::sync::Arc;
-
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use either::Either;
use hir_expand::{db::ExpandDatabase, HirFileId};
use intern::Interned;
use la_arena::ArenaMap;
use syntax::{ast, AstPtr};
+use triomphe::Arc;
use crate::{
- adt::{EnumData, StructData},
attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap},
data::{
+ adt::{EnumData, StructData},
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
TraitAliasData, TraitData, TypeAliasData,
},
@@ -22,15 +21,17 @@ use crate::{
lang_item::{LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility},
- AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
- ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
- LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
- StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
- TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
+ AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
+ EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId,
+ ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc,
+ MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
+ StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
+ UnionLoc, VariantId,
};
#[salsa::query_group(InternDatabaseStorage)]
pub trait InternDatabase: SourceDatabase {
+ // region: items
#[salsa::interned]
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
#[salsa::interned]
@@ -54,19 +55,25 @@ pub trait InternDatabase: SourceDatabase {
#[salsa::interned]
fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
#[salsa::interned]
- fn intern_block(&self, loc: BlockLoc) -> BlockId;
- #[salsa::interned]
fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
#[salsa::interned]
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
#[salsa::interned]
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
+ // endregion: items
+
+ #[salsa::interned]
+ fn intern_block(&self, loc: BlockLoc) -> BlockId;
+ #[salsa::interned]
+ fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
+ #[salsa::interned]
+ fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
}
#[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
#[salsa::input]
- fn enable_proc_attr_macros(&self) -> bool;
+ fn expand_proc_attr_macros(&self) -> bool;
#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
@@ -94,7 +101,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
/// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
/// return a `DefMap` containing `inner`.
#[salsa::invoke(DefMap::block_def_map_query)]
- fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>;
+ fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
+
+ // region:data
#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -151,6 +160,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ProcMacroData::proc_macro_data_query)]
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
+ // endregion:data
+
#[salsa::invoke(Body::body_with_source_map_query)]
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
@@ -163,6 +174,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(GenericParams::generic_params_query)]
fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
+ // region:attrs
+
#[salsa::invoke(Attrs::variants_attrs_query)]
fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
@@ -182,10 +195,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
#[salsa::invoke(AttrsWithOwner::attrs_query)]
- fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
+ fn attrs(&self, def: AttrDefId) -> Attrs;
- #[salsa::invoke(LangItems::crate_lang_items_query)]
- fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+ #[salsa::transparent]
+ #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
+ fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
+
+ // endregion:attrs
#[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
@@ -193,6 +209,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
+ // region:visibilities
+
#[salsa::invoke(visibility::field_visibilities_query)]
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
@@ -203,9 +221,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(visibility::const_visibility_query)]
fn const_visibility(&self, def: ConstId) -> Visibility;
+ // endregion:visibilities
+
+ #[salsa::invoke(LangItems::crate_lang_items_query)]
+ fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+
#[salsa::transparent]
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
+ #[salsa::transparent]
+ fn recursion_limit(&self, crate_id: CrateId) -> u32;
+
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
}
@@ -228,6 +254,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
}
}
+fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 {
+ db.crate_limits(crate_id).recursion_limit
+}
+
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
let file = db.crate_graph()[crate_id].root_file_id;
let item_tree = db.file_item_tree(file.into());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index 166aa04da..63138aa6a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -21,6 +21,8 @@
//!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence.
+pub mod keys;
+
use std::{
hash::Hash,
marker::PhantomData,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
index f30be6b64..f30be6b64 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
new file mode 100644
index 000000000..a588827c8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -0,0 +1,211 @@
+//! Macro expansion utilities.
+
+use base_db::CrateId;
+use cfg::CfgOptions;
+use drop_bomb::DropBomb;
+use hir_expand::{
+ attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId,
+ InFile, MacroCallId, UnresolvedMacro,
+};
+use limit::Limit;
+use syntax::{ast, Parse, SyntaxNode};
+
+use crate::{
+ attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
+ MacroId, ModuleId,
+};
+
+/// A subset of Expander that only deals with cfg attributes. We only need it to
+/// avoid cyclic queries in crate def map during enum processing.
+#[derive(Debug)]
+pub(crate) struct CfgExpander {
+ cfg_options: CfgOptions,
+ hygiene: Hygiene,
+ krate: CrateId,
+}
+
+#[derive(Debug)]
+pub struct Expander {
+ cfg_expander: CfgExpander,
+ pub(crate) current_file_id: HirFileId,
+ pub(crate) module: ModuleId,
+ /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
+ recursion_depth: u32,
+ recursion_limit: Limit,
+}
+
+impl CfgExpander {
+ pub(crate) fn new(
+ db: &dyn DefDatabase,
+ current_file_id: HirFileId,
+ krate: CrateId,
+ ) -> CfgExpander {
+ let hygiene = Hygiene::new(db.upcast(), current_file_id);
+ let cfg_options = db.crate_graph()[krate].cfg_options.clone();
+ CfgExpander { cfg_options, hygiene, krate }
+ }
+
+ pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+ Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
+ }
+
+ pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
+ let attrs = self.parse_attrs(db, owner);
+ attrs.is_cfg_enabled(&self.cfg_options)
+ }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+}
+
+impl Expander {
+ pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
+ let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
+ let recursion_limit = db.recursion_limit(module.krate);
+ #[cfg(not(test))]
+ let recursion_limit = Limit::new(recursion_limit as usize);
+ // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
+ #[cfg(test)]
+ let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize));
+ Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit }
+ }
+
+ pub fn enter_expand<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ macro_call: ast::MacroCall,
+ resolver: impl Fn(ModPath) -> Option<MacroId>,
+ ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
+ // FIXME: within_limit should support this, instead of us having to extract the error
+ let mut unresolved_macro_err = None;
+
+ let result = self.within_limit(db, |this| {
+ let macro_call = InFile::new(this.current_file_id, &macro_call);
+ match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
+ resolver(path).map(|it| macro_id_to_def_id(db, it))
+ }) {
+ Ok(call_id) => call_id,
+ Err(resolve_err) => {
+ unresolved_macro_err = Some(resolve_err);
+ ExpandResult { value: None, err: None }
+ }
+ }
+ });
+
+ if let Some(err) = unresolved_macro_err {
+ Err(err)
+ } else {
+ Ok(result)
+ }
+ }
+
+ pub fn enter_expand_id<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
+ self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
+ }
+
+ fn enter_expand_inner(
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ error: Option<ExpandError>,
+ ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
+ let macro_file = call_id.as_macro_file();
+ let ExpandResult { value, err } = db.parse_macro_expansion(macro_file);
+
+ ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) }
+ }
+
+ pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
+ self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
+ self.current_file_id = mark.file_id;
+ if self.recursion_depth == u32::MAX {
+ // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
+ // depth only when we get out of the tree.
+ if !self.current_file_id.is_macro() {
+ self.recursion_depth = 0;
+ }
+ } else {
+ self.recursion_depth -= 1;
+ }
+ mark.bomb.defuse();
+ }
+
+ pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
+ LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
+ }
+
+ pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
+ InFile { file_id: self.current_file_id, value }
+ }
+
+ pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+ self.cfg_expander.parse_attrs(db, owner)
+ }
+
+ pub(crate) fn cfg_options(&self) -> &CfgOptions {
+ &self.cfg_expander.cfg_options
+ }
+
+ pub fn current_file_id(&self) -> HirFileId {
+ self.current_file_id
+ }
+
+ pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
+ let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id);
+ Path::from_src(path, &ctx)
+ }
+
+ fn within_limit<F, T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ op: F,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>>
+ where
+ F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
+ {
+ if self.recursion_depth == u32::MAX {
+ // Recursion limit has been reached somewhere in the macro expansion tree. We should
+ // stop expanding other macro calls in this tree, or else this may result in
+ // exponential number of macro expansions, leading to a hang.
+ //
+ // The overflow error should have been reported when it occurred (see the next branch),
+ // so don't return overflow error here to avoid diagnostics duplication.
+ cov_mark::hit!(overflow_but_not_me);
+ return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
+ } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
+ self.recursion_depth = u32::MAX;
+ cov_mark::hit!(your_stack_belongs_to_me);
+ return ExpandResult::only_err(ExpandError::other(
+ "reached recursion limit during macro expansion",
+ ));
+ }
+
+ let ExpandResult { value, err } = op(self);
+ let Some(call_id) = value else {
+ return ExpandResult { value: None, err };
+ };
+
+ Self::enter_expand_inner(db, call_id, err).map(|value| {
+ value.and_then(|InFile { file_id, value }| {
+ let parse = value.cast::<T>()?;
+
+ self.recursion_depth += 1;
+ self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
+ let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
+ let mark =
+ Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
+ Some((mark, parse))
+ })
+ })
+ }
+}
+
+#[derive(Debug)]
+pub struct Mark {
+ file_id: HirFileId,
+ bomb: DropBomb,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 3f4392320..8c49ae1c4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
- /// This is the same as plain, just that paths will start with `self` iprepended f the path
+ /// This is the same as plain, just that paths will start with `self` prepended if the path
/// starts with an identifier that is not a crate.
BySelf,
/// Causes paths to ignore imports in the local module.
@@ -81,7 +81,7 @@ fn find_path_inner(
}
let def_map = from.def_map(db);
- let crate_root = def_map.crate_root(db);
+ let crate_root = def_map.crate_root().into();
// - if the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default();
@@ -183,7 +183,7 @@ fn find_path_for_module(
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = crate_root.def_map(db);
- for (name, &def_id) in root_def_map.extern_prelude() {
+ for (name, def_id) in root_def_map.extern_prelude() {
if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone());
@@ -374,7 +374,7 @@ fn calculate_best_path(
}
}
if let Some(module) = item.module(db) {
- if module.def_map(db).block_id().is_some() && prefixed.is_some() {
+ if module.containing_block().is_some() && prefixed.is_some() {
cov_mark::hit!(prefixed_in_block_expression);
prefixed = Some(PrefixKind::Plain);
}
@@ -454,7 +454,7 @@ fn find_local_import_locations(
worklist.push(ancestor);
}
- let def_map = def_map.crate_root(db).def_map(db);
+ let def_map = def_map.crate_root().def_map(db);
let mut seen: FxHashSet<_> = FxHashSet::default();
@@ -543,6 +543,7 @@ mod tests {
module.local_id,
&mod_path,
crate::item_scope::BuiltinShadowMode::Module,
+ None,
)
.0
.take_types()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index e4912fa8a..f19c3f028 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -12,16 +12,17 @@ use hir_expand::{
use intern::Interned;
use la_arena::{Arena, ArenaMap, Idx};
use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
+use triomphe::Arc;
use crate::{
- body::{Expander, LowerCtx},
child_by_source::ChildBySource,
db::DefDatabase,
- dyn_map::DynMap,
- keys,
+ dyn_map::{keys, DynMap},
+ expander::Expander,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@@ -153,7 +154,6 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query");
-
macro_rules! id_to_generics {
($id:ident) => {{
let id = $id.lookup(db).id;
@@ -176,8 +176,10 @@ impl GenericParams {
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
// causes a reparse after the `ItemTree` has been created.
- let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
- for (_, param) in &func_data.params {
+ let mut expander = Lazy::new(|| {
+ (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
+ });
+ for param in &func_data.params {
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
}
@@ -329,7 +331,7 @@ impl GenericParams {
pub(crate) fn fill_implicit_impl_trait_args(
&mut self,
db: &dyn DefDatabase,
- expander: &mut impl DerefMut<Target = Expander>,
+ exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
type_ref: &TypeRef,
) {
type_ref.walk(&mut |type_ref| {
@@ -349,14 +351,28 @@ impl GenericParams {
}
if let TypeRef::Macro(mc) = type_ref {
let macro_call = mc.to_node(db.upcast());
- match expander.enter_expand::<ast::Type>(db, macro_call) {
- Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = LowerCtx::new(db, expander.current_file_id());
- let type_ref = TypeRef::from_ast(&ctx, expanded);
- self.fill_implicit_impl_trait_args(db, expander, &type_ref);
- expander.exit(db, mark);
- }
- _ => {}
+ let (def_map, expander) = &mut **exp;
+
+ let module = expander.module.local_id;
+ let resolver = |path| {
+ def_map
+ .resolve_path(
+ db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ };
+ if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
+ expander.enter_expand(db, macro_call, resolver)
+ {
+ let ctx = expander.ctx(db);
+ let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
+ self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
+ exp.1.exit(db, mark);
}
}
});
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 19fa6b254..500e88006 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -12,26 +12,29 @@
//!
//! See also a neighboring `body` module.
+pub mod type_ref;
+
use std::fmt;
use hir_expand::name::Name;
use intern::Interned;
use la_arena::{Idx, RawIdx};
use smallvec::SmallVec;
+use syntax::ast;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- BlockId,
+ BlockId, ConstBlockId,
};
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
-pub type ExprId = Idx<Expr>;
-
pub type BindingId = Idx<Binding>;
+pub type ExprId = Idx<Expr>;
+
/// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
pub enum Literal {
String(Box<str>),
ByteString(Box<[u8]>),
+ CString(Box<str>),
Char(char),
Bool(bool),
Int(i128, Option<BuiltinInt>),
@@ -93,6 +97,66 @@ pub enum Literal {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+/// Used in range patterns.
+pub enum LiteralOrConst {
+ Literal(Literal),
+ Const(Path),
+}
+
+impl Literal {
+ pub fn negate(self) -> Option<Self> {
+ if let Literal::Int(i, k) = self {
+ Some(Literal::Int(-i, k))
+ } else {
+ None
+ }
+ }
+}
+
+impl From<ast::LiteralKind> for Literal {
+ fn from(ast_lit_kind: ast::LiteralKind) -> Self {
+ use ast::LiteralKind;
+ match ast_lit_kind {
+ // FIXME: these should have actual values filled in, but unsure on perf impact
+ LiteralKind::IntNumber(lit) => {
+ if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
+ Literal::Float(
+ FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
+ builtin,
+ )
+ } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
+ Literal::Uint(lit.value().unwrap_or(0), builtin)
+ } else {
+ let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
+ Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+ }
+ }
+ LiteralKind::FloatNumber(lit) => {
+ let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
+ Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+ }
+ LiteralKind::ByteString(bs) => {
+ let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::ByteString(text)
+ }
+ LiteralKind::String(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::String(text)
+ }
+ LiteralKind::CString(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::CString(text)
+ }
+ LiteralKind::Byte(b) => {
+ Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+ }
+ LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
+ LiteralKind::Bool(val) => Literal::Bool(val),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
Missing,
@@ -107,28 +171,19 @@ pub enum Expr {
expr: ExprId,
},
Block {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
label: Option<LabelId>,
},
- TryBlock {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
Async {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
- Const {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
+ Const(ConstBlockId),
Unsafe {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
@@ -141,12 +196,6 @@ pub enum Expr {
body: ExprId,
label: Option<LabelId>,
},
- For {
- iterable: ExprId,
- pat: PatId,
- body: ExprId,
- label: Option<LabelId>,
- },
Call {
callee: ExprId,
args: Box<[ExprId]>,
@@ -163,11 +212,11 @@ pub enum Expr {
arms: Box<[MatchArm]>,
},
Continue {
- label: Option<Name>,
+ label: Option<LabelId>,
},
Break {
expr: Option<ExprId>,
- label: Option<Name>,
+ label: Option<LabelId>,
},
Return {
expr: Option<ExprId>,
@@ -192,9 +241,6 @@ pub enum Expr {
Await {
expr: ExprId,
},
- Try {
- expr: ExprId,
- },
Cast {
expr: ExprId,
type_ref: Interned<TypeRef>,
@@ -231,6 +277,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>,
body: ExprId,
closure_kind: ClosureKind,
+ capture_by: CaptureBy,
},
Tuple {
exprs: Box<[ExprId]>,
@@ -249,6 +296,14 @@ pub enum ClosureKind {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureBy {
+ /// `move |x| y + x`.
+ Value,
+ /// `move` keyword was not specified.
+ Ref,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability {
Static,
Movable,
@@ -302,11 +357,10 @@ impl Expr {
Expr::Let { expr, .. } => {
f(*expr);
}
+ Expr::Const(_) => (),
Expr::Block { statements, tail, .. }
- | Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. }
- | Expr::Const { statements, tail, .. } => {
+ | Expr::Async { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, else_branch, .. } => {
@@ -329,10 +383,6 @@ impl Expr {
f(*condition);
f(*body);
}
- Expr::For { iterable, body, .. } => {
- f(*iterable);
- f(*body);
- }
Expr::Call { callee, args, .. } => {
f(*callee);
args.iter().copied().for_each(f);
@@ -383,7 +433,6 @@ impl Expr {
}
Expr::Field { expr, .. }
| Expr::Await { expr }
- | Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. }
@@ -438,10 +487,21 @@ impl BindingAnnotation {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum BindingProblems {
+ /// https://doc.rust-lang.org/stable/error_codes/E0416.html
+ BoundMoreThanOnce,
+ /// https://doc.rust-lang.org/stable/error_codes/E0409.html
+ BoundInconsistently,
+ /// https://doc.rust-lang.org/stable/error_codes/E0408.html
+ NotBoundAcrossAll,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Binding {
pub name: Name,
pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>,
+ pub problems: Option<BindingProblems>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -458,7 +518,7 @@ pub enum Pat {
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
Or(Box<[PatId]>),
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
- Range { start: ExprId, end: ExprId },
+ Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
Path(Box<Path>),
Lit(ExprId),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 8e30f429a..fa1f4933a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -1,9 +1,11 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
+use core::fmt;
use std::fmt::Write;
use hir_expand::{
+ db::ExpandDatabase,
name::{AsName, Name},
AstId,
};
@@ -11,9 +13,9 @@ use intern::Interned;
use syntax::ast::{self, HasName};
use crate::{
- body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
- expr::Literal,
+ hir::Literal,
+ lower::LowerCtx,
path::Path,
};
@@ -116,7 +118,7 @@ pub enum TypeRef {
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
// FIXME: for full const generics, the latter element (length) here is going to have to be an
// expression that is further lowered later in hir_ty.
- Array(Box<TypeRef>, ConstRefOrPath),
+ Array(Box<TypeRef>, ConstRef),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -184,11 +186,7 @@ impl TypeRef {
TypeRef::RawPtr(Box::new(inner_ty), mutability)
}
ast::Type::ArrayType(inner) => {
- // FIXME: This is a hack. We should probably reuse the machinery of
- // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
- // `hir_ty` level, which would allow knowing the type of:
- // let v: [u8; 2 + 2] = [0u8; 4];
- let len = ConstRefOrPath::from_expr_opt(inner.expr());
+ let len = ConstRef::from_const_arg(ctx, inner.const_arg());
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
}
ast::Type::SliceType(inner) => {
@@ -378,69 +376,84 @@ impl TypeBound {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRefOrPath {
- Scalar(ConstRef),
+pub enum ConstRef {
+ Scalar(LiteralConstRef),
Path(Name),
+ Complex(AstId<ast::ConstArg>),
}
-impl std::fmt::Display for ConstRefOrPath {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- ConstRefOrPath::Scalar(s) => s.fmt(f),
- ConstRefOrPath::Path(n) => n.fmt(f),
+impl ConstRef {
+ pub(crate) fn from_const_arg(lower_ctx: &LowerCtx<'_>, arg: Option<ast::ConstArg>) -> Self {
+ if let Some(arg) = arg {
+ let ast_id = lower_ctx.ast_id(&arg);
+ if let Some(expr) = arg.expr() {
+ return Self::from_expr(expr, ast_id);
+ }
}
+ Self::Scalar(LiteralConstRef::Unknown)
}
-}
-impl ConstRefOrPath {
- pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
- match expr {
- Some(x) => Self::from_expr(x),
- None => Self::Scalar(ConstRef::Unknown),
+ pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
+ struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
+ impl fmt::Display for Display<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.1 {
+ ConstRef::Scalar(s) => s.fmt(f),
+ ConstRef::Path(n) => n.display(self.0).fmt(f),
+ ConstRef::Complex(_) => f.write_str("{const}"),
+ }
+ }
}
+ Display(db, self)
}
- // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
- // parse stage.
- fn from_expr(expr: ast::Expr) -> Self {
+ // We special case literals and single identifiers, to speed up things.
+ fn from_expr(expr: ast::Expr, ast_id: Option<AstId<ast::ConstArg>>) -> Self {
+ fn is_path_ident(p: &ast::PathExpr) -> bool {
+ let Some(path) = p.path() else {
+ return false;
+ };
+ if path.coloncolon_token().is_some() {
+ return false;
+ }
+ if let Some(s) = path.segment() {
+ if s.coloncolon_token().is_some() || s.generic_arg_list().is_some() {
+ return false;
+ }
+ }
+ true
+ }
match expr {
- ast::Expr::PathExpr(p) => {
+ ast::Expr::PathExpr(p) if is_path_ident(&p) => {
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
Some(x) => Self::Path(x.as_name()),
- None => Self::Scalar(ConstRef::Unknown),
+ None => Self::Scalar(LiteralConstRef::Unknown),
}
}
- ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
- Some(ast::UnaryOp::Neg) => {
- let unsigned = Self::from_expr_opt(prefix_expr.expr());
- // Add sign
- match unsigned {
- Self::Scalar(ConstRef::UInt(num)) => {
- Self::Scalar(ConstRef::Int(-(num as i128)))
- }
- other => other,
- }
- }
- _ => Self::from_expr_opt(prefix_expr.expr()),
- },
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
ast::LiteralKind::IntNumber(num) => {
- num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
+ num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
}
ast::LiteralKind::Char(c) => {
- c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
+ c.value().map(LiteralConstRef::Char).unwrap_or(LiteralConstRef::Unknown)
}
- ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
- _ => ConstRef::Unknown,
+ ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
+ _ => LiteralConstRef::Unknown,
}),
- _ => Self::Scalar(ConstRef::Unknown),
+ _ => {
+ if let Some(ast_id) = ast_id {
+ Self::Complex(ast_id)
+ } else {
+ Self::Scalar(LiteralConstRef::Unknown)
+ }
+ }
}
}
}
-/// A concrete constant value
+/// A literal constant value
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRef {
+pub enum LiteralConstRef {
Int(i128),
UInt(u128),
Bool(bool),
@@ -454,18 +467,20 @@ pub enum ConstRef {
Unknown,
}
-impl ConstRef {
+impl LiteralConstRef {
pub fn builtin_type(&self) -> BuiltinType {
match self {
- ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
- ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
- ConstRef::Char(_) => BuiltinType::Char,
- ConstRef::Bool(_) => BuiltinType::Bool,
+ LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => {
+ BuiltinType::Uint(BuiltinUint::U128)
+ }
+ LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+ LiteralConstRef::Char(_) => BuiltinType::Char,
+ LiteralConstRef::Bool(_) => BuiltinType::Bool,
}
}
}
-impl From<Literal> for ConstRef {
+impl From<Literal> for LiteralConstRef {
fn from(literal: Literal) -> Self {
match literal {
Literal::Char(c) => Self::Char(c),
@@ -477,14 +492,14 @@ impl From<Literal> for ConstRef {
}
}
-impl std::fmt::Display for ConstRef {
+impl std::fmt::Display for LiteralConstRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
- ConstRef::Int(num) => num.fmt(f),
- ConstRef::UInt(num) => num.fmt(f),
- ConstRef::Bool(flag) => flag.fmt(f),
- ConstRef::Char(c) => write!(f, "'{c}'"),
- ConstRef::Unknown => f.write_char('_'),
+ LiteralConstRef::Int(num) => num.fmt(f),
+ LiteralConstRef::UInt(num) => num.fmt(f),
+ LiteralConstRef::Bool(flag) => flag.fmt(f),
+ LiteralConstRef::Char(c) => write!(f, "'{c}'"),
+ LiteralConstRef::Unknown => f.write_char('_'),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 4f1f6000d..48532655e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -1,6 +1,6 @@
//! A map of all publicly exported items in a crate.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use fst::{self, Streamer};
@@ -8,10 +8,11 @@ use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap};
use itertools::Itertools;
use rustc_hash::{FxHashSet, FxHasher};
+use triomphe::Arc;
use crate::{
- db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
- ModuleId, TraitId,
+ db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
+ ModuleDefId, ModuleId, TraitId,
};
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -32,13 +33,23 @@ pub struct ImportPath {
pub segments: Vec<Name>,
}
-impl fmt::Display for ImportPath {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.segments.iter().format("::"), f)
+impl ImportPath {
+ pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a {
+ struct Display<'a> {
+ db: &'a dyn DefDatabase,
+ path: &'a ImportPath,
+ }
+ impl fmt::Display for Display<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(
+ &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"),
+ f,
+ )
+ }
+ }
+ Display { db, path: self }
}
-}
-impl ImportPath {
fn len(&self) -> usize {
self.segments.len()
}
@@ -75,7 +86,7 @@ impl ImportMap {
let mut importables = import_map
.map
.iter()
- .map(|(item, info)| (item, fst_path(&info.path)))
+ .map(|(item, info)| (item, fst_path(db, &info.path)))
.collect::<Vec<_>>();
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
@@ -112,6 +123,25 @@ impl ImportMap {
self.map.get(&item)
}
+ #[cfg(test)]
+ fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
+ let mut importable_paths: Vec<_> = self
+ .map
+ .iter()
+ .map(|(item, info)| {
+ let ns = match item {
+ ItemInNs::Types(_) => "t",
+ ItemInNs::Values(_) => "v",
+ ItemInNs::Macros(_) => "m",
+ };
+ format!("- {} ({ns})", info.path.display(db))
+ })
+ .collect();
+
+ importable_paths.sort();
+ importable_paths.join("\n")
+ }
+
fn collect_trait_assoc_items(
&mut self,
db: &dyn DefDatabase,
@@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
// We look only into modules that are public(ly reexported), starting with the crate root.
let empty = ImportPath { segments: vec![] };
- let root = def_map.module_id(def_map.root());
+ let root = def_map.module_id(DefMap::ROOT);
let mut worklist = vec![(root, empty)];
while let Some((module, mod_path)) = worklist.pop() {
let ext_def_map;
@@ -233,13 +263,10 @@ impl fmt::Debug for ImportMap {
let mut importable_paths: Vec<_> = self
.map
.iter()
- .map(|(item, info)| {
- let ns = match item {
- ItemInNs::Types(_) => "t",
- ItemInNs::Values(_) => "v",
- ItemInNs::Macros(_) => "m",
- };
- format!("- {} ({ns})", info.path)
+ .map(|(item, _)| match item {
+ ItemInNs::Types(it) => format!("- {it:?} (t)",),
+ ItemInNs::Values(it) => format!("- {it:?} (v)",),
+ ItemInNs::Macros(it) => format!("- {it:?} (m)",),
})
.collect();
@@ -248,9 +275,9 @@ impl fmt::Debug for ImportMap {
}
}
-fn fst_path(path: &ImportPath) -> String {
+fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
let _p = profile::span("fst_path");
- let mut s = path.to_string();
+ let mut s = path.display(db).to_string();
s.make_ascii_lowercase();
s
}
@@ -343,7 +370,12 @@ impl Query {
self
}
- fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
+ fn import_matches(
+ &self,
+ db: &dyn DefDatabase,
+ import: &ImportInfo,
+ enforce_lowercase: bool,
+ ) -> bool {
let _p = profile::span("import_map::Query::import_matches");
if import.is_trait_assoc_item {
if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
@@ -354,9 +386,9 @@ impl Query {
}
let mut input = if import.is_trait_assoc_item || self.name_only {
- import.path.segments.last().unwrap().to_string()
+ import.path.segments.last().unwrap().display(db.upcast()).to_string()
} else {
- import.path.to_string()
+ import.path.display(db).to_string()
};
if enforce_lowercase || !self.case_sensitive {
input.make_ascii_lowercase();
@@ -421,25 +453,27 @@ pub fn search_dependencies(
let importables = &import_map.importables[indexed_value.value as usize..];
let common_importable_data = &import_map.map[&importables[0]];
- if !query.import_matches(common_importable_data, true) {
+ if !query.import_matches(db, common_importable_data, true) {
continue;
}
// Path shared by the importable items in this group.
- let common_importables_path_fst = fst_path(&common_importable_data.path);
+ let common_importables_path_fst = fst_path(db, &common_importable_data.path);
// Add the items from this `ModPath` group. Those are all subsequent items in
// `importables` whose paths match `path`.
let iter = importables
.iter()
.copied()
- .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
+ .take_while(|item| {
+ common_importables_path_fst == fst_path(db, &import_map.map[item].path)
+ })
.filter(|&item| match item_import_kind(item) {
Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
None => true,
})
.filter(|item| {
!query.case_sensitive // we've already checked the common importables path case-insensitively
- || query.import_matches(&import_map.map[item], false)
+ || query.import_matches(db, &import_map.map[item], false)
});
res.extend(iter);
@@ -472,7 +506,7 @@ mod tests {
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
use expect_test::{expect, Expect};
- use crate::{test_db::TestDB, ItemContainerId, Lookup};
+ use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
use super::*;
@@ -496,7 +530,7 @@ mod tests {
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
Some(assoc_item_path) => (assoc_item_path, "a"),
None => (
- dependency_imports.path_of(dependency)?.to_string(),
+ dependency_imports.path_of(dependency)?.display(&db).to_string(),
match dependency {
ItemInNs::Types(ModuleDefId::FunctionId(_))
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
@@ -547,7 +581,11 @@ mod tests {
None
}
})?;
- return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
+ return Some(format!(
+ "{}::{}",
+ dependency_imports.path_of(trait_)?.display(db),
+ assoc_item_name.display(db.upcast())
+ ));
}
None
}
@@ -587,7 +625,7 @@ mod tests {
let map = db.import_map(krate);
- Some(format!("{name}:\n{map:?}\n"))
+ Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
})
.sorted()
.collect::<String>();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 991e44703..2001fb29a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -4,7 +4,7 @@
use std::collections::hash_map::Entry;
use base_db::CrateId;
-use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
+use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
use itertools::Itertools;
use once_cell::sync::Lazy;
use profile::Count;
@@ -334,10 +334,6 @@ impl ItemScope {
)
}
- pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, SmallVec<[MacroId; 1]>> {
- self.legacy_macros.clone()
- }
-
/// Marks everything that is not a procedural macro as private to `this_module`.
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types
@@ -358,12 +354,16 @@ impl ItemScope {
}
}
- pub(crate) fn dump(&self, buf: &mut String) {
+ pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) {
let mut entries: Vec<_> = self.resolutions().collect();
entries.sort_by_key(|(name, _)| name.clone());
for (name, def) in entries {
- format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
+ format_to!(
+ buf,
+ "{}:",
+ name.map_or("_".to_string(), |name| name.display(db).to_string())
+ );
if def.types.is_some() {
buf.push_str(" t");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 9da5b2d47..e74b71888 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -40,7 +40,6 @@ use std::{
hash::{Hash, Hasher},
marker::PhantomData,
ops::Index,
- sync::Arc,
};
use ast::{AstNode, HasName, StructKind};
@@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use stdx::never;
use syntax::{ast, match_ast, SyntaxKind};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -107,10 +107,7 @@ pub struct ItemTree {
impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
- let syntax = match db.parse_or_expand(file_id) {
- Some(node) => node,
- None => return Default::default(),
- };
+ let syntax = db.parse_or_expand(file_id);
if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
{
// FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
@@ -169,8 +166,8 @@ impl ItemTree {
Attrs::filter(db, krate, self.raw_attrs(of).clone())
}
- pub fn pretty_print(&self) -> String {
- pretty::print_item_tree(self)
+ pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
+ pretty::print_item_tree(db.upcast(), self)
}
fn data(&self) -> &ItemTreeData {
@@ -600,19 +597,18 @@ pub struct Function {
pub abi: Option<Interned<str>>,
pub params: IdxRange<Param>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Param {
- Normal(Option<Name>, Interned<TypeRef>),
+ Normal(Interned<TypeRef>),
Varargs,
}
bitflags::bitflags! {
- #[derive(Default)]
+ #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub(crate) struct FnFlags: u8 {
const HAS_SELF_PARAM = 1 << 0;
const HAS_BODY = 1 << 1;
@@ -721,7 +717,6 @@ pub struct Mod {
pub enum ModKind {
/// `mod m { ... }`
Inline { items: Box<[ModItem]> },
-
/// `mod m;`
Outline,
}
@@ -895,10 +890,6 @@ impl ModItem {
}
}
- pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
- N::id_from_mod_item(self)
- }
-
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
match self {
ModItem::Import(it) => tree[it.index].ast_id().upcast(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 77b186f8e..46633667e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -1,6 +1,6 @@
//! AST -> `ItemTree` lowering code.
-use std::{collections::hash_map::Entry, sync::Arc};
+use std::collections::hash_map::Entry;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
@@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase,
tree: ItemTree,
source_ast_id_map: Arc<AstIdMap>,
- body_ctx: crate::body::LowerCtx<'a>,
+ body_ctx: crate::lower::LowerCtx<'a>,
}
impl<'a> Ctx<'a> {
@@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db,
tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file),
- body_ctx: crate::body::LowerCtx::new(db, file),
+ body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
}
}
@@ -293,7 +293,7 @@ impl<'a> Ctx<'a> {
}
};
let ty = Interned::new(self_type);
- let idx = self.data().params.alloc(Param::Normal(None, ty));
+ let idx = self.data().params.alloc(Param::Normal(ty));
self.add_attrs(
idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref);
- let mut pat = param.pat();
- // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
- // pattern names at all
- let name = 'name: loop {
- match pat {
- Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
- Some(ast::Pat::IdentPat(ident)) => {
- break 'name ident.name().map(|it| it.as_name())
- }
- _ => break 'name None,
- }
- };
- self.data().params.alloc(Param::Normal(name, ty))
+ self.data().params.alloc(Param::Normal(ty))
}
};
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
@@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
None => TypeRef::unit(),
};
- let (ret_type, async_ret_type) = if func.async_token().is_some() {
- let async_ret_type = ret_type.clone();
+ let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
- (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
+ TypeRef::ImplTrait(vec![ty_bound])
} else {
- (ret_type, None)
+ ret_type
};
let abi = func.abi().map(lower_abi);
@@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
abi,
params,
ret_type: Interned::new(ret_type),
- async_ret_type: async_ret_type.map(Interned::new),
ast_id,
flags,
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 5f2999796..e873316a5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,6 +2,8 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
+
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -10,8 +12,8 @@ use crate::{
use super::*;
-pub(super) fn print_item_tree(tree: &ItemTree) -> String {
- let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true };
+pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+ let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
p.print_attrs(attrs, true);
@@ -43,6 +45,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
self,
"#{}[{}{}]",
inner,
- attr.path,
+ attr.path.display(self.db),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
);
}
@@ -102,7 +105,7 @@ impl<'a> Printer<'a> {
fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] {
- RawVisibility::Module(path) => w!(self, "pub({}) ", path),
+ RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
RawVisibility::Public => w!(self, "pub "),
};
}
@@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path);
+ w!(self, "{}", path.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path);
+ w!(self, "{}::", path.display(self.db));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix);
+ w!(self, "{}::", prefix.display(self.db));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "extern crate {}", name);
+ w!(self, "extern crate {}", name.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
@@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
abi,
params,
ret_type,
- async_ret_type: _,
ast_id: _,
flags,
} = &self.tree[it];
@@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
- w!(self, "fn {}", name);
+ w!(self, "fn {}", name.display(self.db));
self.print_generic_params(explicit_generic_params);
w!(self, "(");
if !params.is_empty() {
self.indented(|this| {
- for (i, param) in params.clone().enumerate() {
+ for param in params.clone() {
this.print_attrs_of(param);
match &this.tree[param] {
- Param::Normal(name, ty) => {
- match name {
- Some(name) => w!(this, "{}: ", name),
- None => w!(this, "_: "),
+ Param::Normal(ty) => {
+ if flags.contains(FnFlags::HAS_SELF_PARAM) {
+ w!(this, "self: ");
}
this.print_type_ref(ty);
- w!(this, ",");
- if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
- wln!(this, " // self");
- } else {
- wln!(this);
- }
+ wln!(this, ",");
}
Param::Varargs => {
wln!(this, "...");
@@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "struct {}", name);
+ w!(self, "struct {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "union {}", name);
+ w!(self, "union {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "enum {}", name);
+ w!(self, "enum {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant);
- w!(this, "{}", name);
+ w!(this, "{}", name.display(self.db));
this.print_fields(fields);
wln!(this, ",");
}
@@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name);
+ w!(self, "{}: ", name.display(self.db));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
if *is_auto {
w!(self, "auto ");
}
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -418,7 +414,7 @@ impl<'a> Printer<'a> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
self.print_visibility(*visibility);
- w!(self, "type {}", name);
+ w!(self, "type {}", name.display(self.db));
self.print_generic_params(generic_params);
if !bounds.is_empty() {
w!(self, ": ");
@@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name);
+ w!(self, "mod {}", name.display(self.db));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
}
ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
- wln!(self, "{}!(...);", path);
+ wln!(self, "{}!(...);", path.display(self.db));
}
ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it];
- wln!(self, "macro_rules! {} {{ ... }}", name);
+ wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
}
ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- wln!(self, "macro {} {{ ... }}", name);
+ wln!(self, "macro {} {{ ... }}", name.display(self.db));
}
}
@@ -470,15 +466,15 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, type_ref: &TypeRef) {
- print_type_ref(type_ref, self).unwrap();
+ print_type_ref(self.db, type_ref, self).unwrap();
}
fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
- print_type_bounds(bounds, self).unwrap();
+ print_type_bounds(self.db, bounds, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_generic_params(&mut self, params: &GenericParams) {
@@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
w!(self, ", ");
}
first = false;
- w!(self, "{}", lt.name);
+ w!(self, "{}", lt.name.display(self.db));
}
for (idx, x) in params.type_or_consts.iter() {
if !first {
@@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
first = false;
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_anon_{}", idx.into_raw()),
},
TypeOrConstParamData::ConstParamData(konst) => {
- w!(self, "const {}: ", konst.name);
+ w!(self, "const {}: ", konst.name.display(self.db));
self.print_type_ref(&konst.ty);
}
}
@@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => {
- wln!(this, "{}: {},", target.name, bound.name);
+ wln!(
+ this,
+ "{}: {},",
+ target.name.display(self.db),
+ bound.name.display(self.db)
+ );
continue;
}
WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt);
+ w!(this, "{}", lt.display(self.db));
}
w!(this, "> ");
(target, bound)
@@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => w!(this, "{}", name),
+ Some(name) => w!(this, "{}", name.display(self.db)),
None => w!(this, "_anon_{}", id.into_raw()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index e30d9652b..5ded4b6b2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
fn check(ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let item_tree = db.file_item_tree(file_id.into());
- let pretty = item_tree.pretty_print();
+ let pretty = item_tree.pretty_print(&db);
expect.assert_eq(&pretty);
}
@@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
fn method(&self);
}
"#,
- expect![[r##"
+ expect![[r#"
pub static mut ST: () = _;
pub(self) const _: Anon = _;
@@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
#[inner_attr_in_fn]
pub(self) fn f(
#[attr]
- arg: u8,
- _: (),
+ u8,
+ (),
) -> () { ... }
pub(self) trait Tr<Self>
@@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
pub(self) type Assoc: AssocBound = Default;
pub(self) fn method(
- _: &Self, // self
+ self: &Self,
) -> ();
}
- "##]],
+ "#]],
);
}
@@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
T: 'b
{
pub(self) fn f<G>(
- arg: impl Copy,
+ impl Copy,
) -> impl Copy
where
G: 'a { ... }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index d338bb412..0e9ac58fb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,14 +2,13 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
-use std::sync::Arc;
-
use rustc_hash::FxHashMap;
use syntax::SmolStr;
+use triomphe::Arc;
use crate::{
- db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
- ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+ FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -200,7 +199,7 @@ pub enum GenericRequirement {
macro_rules! language_item_table {
(
- $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
+ $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
) => {
/// A representation of all the valid language items in Rust.
@@ -221,11 +220,6 @@ macro_rules! language_item_table {
}
/// Opposite of [`LangItem::name`]
- pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
- Self::from_str(name.as_str()?)
- }
-
- /// Opposite of [`LangItem::name`]
pub fn from_str(name: &str) -> Option<Self> {
match name {
$( stringify!($name) => Some(LangItem::$variant), )*
@@ -236,84 +230,100 @@ macro_rules! language_item_table {
}
}
+impl LangItem {
+ /// Opposite of [`LangItem::name`]
+ pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
+ Self::from_str(name.as_str()?)
+ }
+
+ pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
+ let t = db.lang_item(start_crate, *self)?;
+ Some(Path::LangItem(t))
+ }
+}
+
language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements;
- Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
- Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
+ Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
- StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
+ StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
- StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
- Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
- Clone, clone, clone_trait, Target::Trait, GenericRequirement::None;
- Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
- DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
+ StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
+ Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
+ Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
+ Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
+ DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the [`DiscriminantKind`] trait.
- Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+ Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+
+ PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
+ Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
+ DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
- PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
- Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
- DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
+ Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
- Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- Drop, drop, drop_trait, Target::Trait, GenericRequirement::None;
- Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None;
+ Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
+ Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
- CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
- DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
+ CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
+ DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
// language items relating to transmutability
- TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
- TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
-
- Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1);
- Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
- Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
- Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1);
- Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
- Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
- Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0);
- BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
- Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
- Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
- AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1);
- IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
-
- UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
- VaList, va_list, va_list, Target::Struct, GenericRequirement::None;
-
- Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
- Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None;
-
- Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
- FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
- FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
-
- FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
-
- Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
- GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None;
- Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
- Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None;
- Pin, pin, pin_type, Target::Struct, GenericRequirement::None;
-
- PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
- PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
+ TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
+
+ Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
+ Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
+ Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
+ Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1);
+ Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
+ Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
+ Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0);
+ BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
+ AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
+ IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
+ VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
+
+ Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
+ Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
+
+ Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
+
+ Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
+ GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
+ Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
+ Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
+
+ PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
+ PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
@@ -322,92 +332,103 @@ language_item_table! {
// in the sense that a crate is not required to have it defined to use it, but a final product
// is required to define it somewhere. Additionally, there are restrictions on crates that use
// a weak lang item, but do not have it defined.
- Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
- PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
- PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None;
- ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
- PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None;
- PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None;
- PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
- PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
+ Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
+ PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
+ ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
+ PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
+ PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
+ PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
/// libstd panic entry point. Necessary for const eval to be able to catch it
- BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
+ BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
- ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
- BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
- DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
- AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
+ // Lang items needed for `format_args!()`.
+ FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
+ FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
+ FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
+ FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;
+ FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None;
+ FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
- Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1);
+ ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
+ BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
+ DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
+ AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
- EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
- EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
+ Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
- OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
+ EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
+ EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
- PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
+ OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
- ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+ PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
- MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
+ ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+
+ MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
/// Align offset for stride != 1; must not panic.
- AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
+ AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
- Termination, termination, termination, Target::Trait, GenericRequirement::None;
+ Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
- Try, Try, try_trait, Target::Trait, GenericRequirement::None;
+ Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None;
- Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
+ Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
- SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
// Language items from AST lowering
- TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+ TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+
+ PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
- PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
+ ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
- Poll, Poll, poll, Target::Enum, GenericRequirement::None;
- PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
- PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
+ Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
+ PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
+ PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
- ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
- GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None;
-
- Context, Context, context, Target::Struct, GenericRequirement::None;
- FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
+ GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
- FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ Context, sym::Context, context, Target::Struct, GenericRequirement::None;
+ FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None;
- OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None;
+ Option, sym::Option, option_type, Target::Enum, GenericRequirement::None;
+ OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None;
+ OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None;
- ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
- ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None;
+ ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
+ ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None;
- ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
- ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None;
- IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
+ IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
- PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
- RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- Range, Range, range_struct, Target::Struct, GenericRequirement::None;
- RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
+ RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
+ RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
+ RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
- String, String, string, Target::Struct, GenericRequirement::None;
+ String, sym::String, string, Target::Struct, GenericRequirement::None;
+ CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs b/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
deleted file mode 100644
index 49b1190ad..000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! Definitions needed for computing data layout of types.
-
-use std::cmp;
-
-use la_arena::{Idx, RawIdx};
-pub use rustc_abi::{
- Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
- LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
- TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
-};
-
-use crate::LocalEnumVariantId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
-
-impl rustc_index::vec::Idx for RustcEnumVariantIdx {
- fn new(idx: usize) -> Self {
- RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
- }
-
- fn index(self) -> usize {
- u32::from(self.0.into_raw()) as usize
- }
-}
-
-pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
-pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
-pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
-
-pub trait IntegerExt {
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError>;
-}
-
-impl IntegerExt for Integer {
- /// Finds the appropriate Integer type and signedness for the given
- /// signed discriminant range and `#[repr]` attribute.
- /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
- /// that shouldn't affect anything, other than maybe debuginfo.
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError> {
- // Theoretically, negative values could be larger in unsigned representation
- // than the unsigned representation of the signed minimum. However, if there
- // are any negative values, the only valid unsigned representation is u128
- // which can fit all i128 values, so the result remains unaffected.
- let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
- let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
-
- if let Some(ity) = repr.int {
- let discr = Integer::from_attr(dl, ity);
- let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
- if discr < fit {
- return Err(LayoutError::UserError(
- "Integer::repr_discr: `#[repr]` hint too small for \
- discriminant range of enum "
- .to_string(),
- ));
- }
- return Ok((discr, ity.is_signed()));
- }
-
- let at_least = if repr.c() {
- // This is usually I32, however it can be different on some platforms,
- // notably hexagon and arm-none/thumb-none
- dl.c_enum_min_size
- } else {
- // repr(Rust) enums try to be as small as possible
- Integer::I8
- };
-
- // If there are no negative values, we can use the unsigned fit.
- Ok(if min >= 0 {
- (cmp::max(unsigned_fit, at_least), false)
- } else {
- (cmp::max(signed_fit, at_least), true)
- })
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum LayoutError {
- UserError(String),
- SizeOverflow,
- TargetLayoutNotAvailable,
- HasPlaceholder,
- NotImplemented,
- Unknown,
-}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 8c2e93f09..9d8b57a0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -18,24 +18,23 @@ pub mod db;
pub mod attr;
pub mod path;
-pub mod type_ref;
pub mod builtin_type;
-pub mod builtin_attr;
pub mod per_ns;
pub mod item_scope;
+pub mod lower;
+pub mod expander;
+
pub mod dyn_map;
-pub mod keys;
pub mod item_tree;
-pub mod adt;
pub mod data;
pub mod generics;
pub mod lang_item;
-pub mod layout;
-pub mod expr;
+pub mod hir;
+pub use self::hir::type_ref;
pub mod body;
pub mod resolver;
@@ -49,6 +48,9 @@ pub mod visibility;
pub mod find_path;
pub mod import_map;
+pub use rustc_abi as layout;
+use triomphe::Arc;
+
#[cfg(test)]
mod test_db;
#[cfg(test)]
@@ -57,7 +59,7 @@ mod pretty;
use std::{
hash::{Hash, Hasher},
- sync::Arc,
+ panic::{RefUnwindSafe, UnwindSafe},
};
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
@@ -67,11 +69,12 @@ use hir_expand::{
builtin_attr_macro::BuiltinAttrExpander,
builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
- eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
+ db::ExpandDatabase,
+ eager::expand_eager_macro_input,
hygiene::Hygiene,
proc_macro::ProcMacroExpander,
- AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
- MacroDefKind, UnresolvedMacro,
+ AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+ MacroDefId, MacroDefKind, UnresolvedMacro,
};
use item_tree::ExternBlock;
use la_arena::Idx;
@@ -82,14 +85,54 @@ use syntax::ast;
use ::tt::token_id as tt;
use crate::{
- adt::VariantData,
builtin_type::BuiltinType,
+ data::adt::VariantData,
item_tree::{
- Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
- Static, Struct, Trait, TraitAlias, TypeAlias, Union,
+ Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static,
+ Struct, Trait, TraitAlias, TypeAlias, Union,
},
};
+/// A `ModuleId` that is always a crate's root module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CrateRootModuleId {
+ krate: CrateId,
+}
+
+impl CrateRootModuleId {
+ pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
+ db.crate_def_map(self.krate)
+ }
+
+ pub fn krate(self) -> CrateId {
+ self.krate
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleId {
+ fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
+ ModuleId { krate, block: None, local_id: DefMap::ROOT }
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleDefId {
+ fn from(value: CrateRootModuleId) -> Self {
+ ModuleDefId::ModuleId(value.into())
+ }
+}
+
+impl TryFrom<ModuleId> for CrateRootModuleId {
+ type Error = ();
+
+ fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
+ if block.is_none() && local_id == DefMap::ROOT {
+ Ok(CrateRootModuleId { krate })
+ } else {
+ Err(())
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModuleId {
krate: CrateId,
@@ -104,13 +147,7 @@ pub struct ModuleId {
impl ModuleId {
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
match self.block {
- Some(block) => {
- db.block_def_map(block).unwrap_or_else(|| {
- // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s,
- // so the `DefMap` here must exist.
- unreachable!("no `block_def_map` for `ModuleId` {:?}", self);
- })
- }
+ Some(block) => db.block_def_map(block),
None => db.crate_def_map(self.krate),
}
}
@@ -236,7 +273,7 @@ pub struct EnumVariantId {
pub local_id: LocalEnumVariantId,
}
-pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
+pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId {
@@ -244,7 +281,7 @@ pub struct FieldId {
pub local_id: LocalFieldId,
}
-pub type LocalFieldId = Idx<adt::FieldData>;
+pub type LocalFieldId = Idx<data::adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId);
@@ -317,8 +354,7 @@ impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macr
pub struct ProcMacroId(salsa::InternId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ProcMacroLoc {
- // FIXME: this should be a crate? or just a crate-root module
- pub container: ModuleId,
+ pub container: CrateRootModuleId,
pub id: ItemTreeId<Function>,
pub expander: ProcMacroExpander,
pub kind: ProcMacroKind,
@@ -478,16 +514,228 @@ impl_from!(
for ModuleDefId
);
+/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
+/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct ConstBlockId(salsa::InternId);
+impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct ConstBlockLoc {
+ /// The parent of the anonymous const block.
+ pub parent: DefWithBodyId,
+ /// The root expression of this const block in the parent body.
+ pub root: hir::ExprId,
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum TypeOwnerId {
+ FunctionId(FunctionId),
+ StaticId(StaticId),
+ ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
+ AdtId(AdtId),
+ TraitId(TraitId),
+ TraitAliasId(TraitAliasId),
+ TypeAliasId(TypeAliasId),
+ ImplId(ImplId),
+ EnumVariantId(EnumVariantId),
+ // FIXME(const-generic-body): ModuleId should not be a type owner. This needs to be fixed to make `TypeOwnerId` actually
+ // useful for assigning ids to in type consts.
+ ModuleId(ModuleId),
+}
+
+impl TypeOwnerId {
+ fn as_generic_def_id(self) -> Option<GenericDefId> {
+ Some(match self {
+ TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x),
+ TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x),
+ TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x),
+ TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x),
+ TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x),
+ TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x),
+ TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x),
+ TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x),
+ TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
+ return None
+ }
+ })
+ }
+}
+
+impl_from!(
+ FunctionId,
+ StaticId,
+ ConstId,
+ InTypeConstId,
+ AdtId,
+ TraitId,
+ TraitAliasId,
+ TypeAliasId,
+ ImplId,
+ EnumVariantId,
+ ModuleId
+ for TypeOwnerId
+);
+
+// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`)
+impl From<DefWithBodyId> for TypeOwnerId {
+ fn from(value: DefWithBodyId) -> Self {
+ match value {
+ DefWithBodyId::FunctionId(x) => x.into(),
+ DefWithBodyId::StaticId(x) => x.into(),
+ DefWithBodyId::ConstId(x) => x.into(),
+ DefWithBodyId::InTypeConstId(x) => x.into(),
+ DefWithBodyId::VariantId(x) => x.into(),
+ }
+ }
+}
+
+impl From<GenericDefId> for TypeOwnerId {
+ fn from(value: GenericDefId) -> Self {
+ match value {
+ GenericDefId::FunctionId(x) => x.into(),
+ GenericDefId::AdtId(x) => x.into(),
+ GenericDefId::TraitId(x) => x.into(),
+ GenericDefId::TraitAliasId(x) => x.into(),
+ GenericDefId::TypeAliasId(x) => x.into(),
+ GenericDefId::ImplId(x) => x.into(),
+ GenericDefId::EnumVariantId(x) => x.into(),
+ GenericDefId::ConstId(x) => x.into(),
+ }
+ }
+}
+
+// FIXME: This should not be a thing
+/// A thing that we want to store in interned ids, but we don't know its type in `hir-def`. This is
+/// currently only used in `InTypeConstId` for storing the type (which has type `Ty` defined in
+/// the `hir-ty` crate) of the constant in its id, which is a temporary hack so we may want
+/// to remove this after removing that.
+pub trait OpaqueInternableThing:
+ std::any::Any + std::fmt::Debug + Sync + Send + UnwindSafe + RefUnwindSafe
+{
+ fn as_any(&self) -> &dyn std::any::Any;
+ fn box_any(&self) -> Box<dyn std::any::Any>;
+ fn dyn_hash(&self, state: &mut dyn Hasher);
+ fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool;
+ fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing>;
+}
+
+impl Hash for dyn OpaqueInternableThing {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.dyn_hash(state);
+ }
+}
+
+impl PartialEq for dyn OpaqueInternableThing {
+ fn eq(&self, other: &Self) -> bool {
+ self.dyn_eq(other)
+ }
+}
+
+impl Eq for dyn OpaqueInternableThing {}
+
+impl Clone for Box<dyn OpaqueInternableThing> {
+ fn clone(&self) -> Self {
+ self.dyn_clone()
+ }
+}
+
+// FIXME(const-generic-body): Use an stable id for in type consts.
+//
+// The current id uses `AstId<ast::ConstArg>` which will be changed by every change in the code. Ideally
+// we should use an id which is relative to the type owner, so that every change will only invalidate the
+// id if it happens inside of the type owner.
+//
+// The solution probably is to have some query on `TypeOwnerId` to traverse its constant children and store
+// their `AstId` in a list (vector or arena), and use the index of that list in the id here. That query probably
+// needs name resolution, and might go far and handles the whole path lowering or type lowering for a `TypeOwnerId`.
+//
+// Whatever path the solution takes, it should answer 3 questions at the same time:
+// * Is the id stable enough?
+// * How to find a constant id using an ast node / position in the source code? This is needed when we want to
+// provide ide functionalities inside an in type const (which we currently don't support) e.g. go to definition
+// for a local defined there. A complex id might have some trouble in this reverse mapping.
+// * How to find the return type of a constant using its id? We have this data when we are doing type lowering
+// and the name of the struct that contains this constant is resolved, so a query that only traverses the
+// type owner by its syntax tree might have a hard time here.
+
+/// A constant in a type as a substitution for const generics (like `Foo<{ 2 + 2 }>`) or as an array
+/// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
+/// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct InTypeConstId(salsa::InternId);
+impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
+
+#[derive(Debug, Hash, Eq, Clone)]
+pub struct InTypeConstLoc {
+ pub id: AstId<ast::ConstArg>,
+ /// The thing this const arg appears in
+ pub owner: TypeOwnerId,
+ pub thing: Box<dyn OpaqueInternableThing>,
+}
+
+impl PartialEq for InTypeConstLoc {
+ fn eq(&self, other: &Self) -> bool {
+ self.id == other.id && self.owner == other.owner && &*self.thing == &*other.thing
+ }
+}
+
+impl InTypeConstId {
+ pub fn source(&self, db: &dyn db::DefDatabase) -> ast::ConstArg {
+ let src = self.lookup(db).id;
+ let file_id = src.file_id;
+ let root = &db.parse_or_expand(file_id);
+ db.ast_id_map(file_id).get(src.value).to_node(root)
+ }
+}
+
+/// A constant, which might appears as a const item, an annonymous const block in expressions
+/// or patterns, or as a constant in types with const generics.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GeneralConstId {
+ ConstId(ConstId),
+ ConstBlockId(ConstBlockId),
+ InTypeConstId(InTypeConstId),
+}
+
+impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId);
+
+impl GeneralConstId {
+ pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
+ match self {
+ GeneralConstId::ConstId(it) => Some(it.into()),
+ GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(),
+ GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(),
+ }
+ }
+
+ pub fn name(self, db: &dyn db::DefDatabase) -> String {
+ match self {
+ GeneralConstId::ConstId(const_id) => db
+ .const_data(const_id)
+ .name
+ .as_ref()
+ .and_then(|x| x.as_str())
+ .unwrap_or("_")
+ .to_owned(),
+ GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
+ GeneralConstId::InTypeConstId(id) => format!("{{in type const {id:?}}}"),
+ }
+ }
+}
+
/// The defs which have a body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
VariantId(EnumVariantId),
}
-impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
+impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
impl From<EnumVariantId> for DefWithBodyId {
fn from(id: EnumVariantId) -> Self {
@@ -502,6 +750,9 @@ impl DefWithBodyId {
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
+ // FIXME: stable rust doesn't allow generics in constants, but we should
+ // use `TypeOwnerId::as_generic_def_id` when it does.
+ DefWithBodyId::InTypeConstId(_) => None,
}
}
}
@@ -691,29 +942,37 @@ impl HasModule for MacroId {
match self {
MacroId::MacroRulesId(it) => it.lookup(db).container,
MacroId::Macro2Id(it) => it.lookup(db).container,
- MacroId::ProcMacroId(it) => it.lookup(db).container,
+ MacroId::ProcMacroId(it) => it.lookup(db).container.into(),
}
}
}
-impl HasModule for DefWithBodyId {
+impl HasModule for TypeOwnerId {
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
- DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
- DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ TypeOwnerId::FunctionId(x) => x.lookup(db).module(db),
+ TypeOwnerId::StaticId(x) => x.lookup(db).module(db),
+ TypeOwnerId::ConstId(x) => x.lookup(db).module(db),
+ TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db),
+ TypeOwnerId::AdtId(x) => x.module(db),
+ TypeOwnerId::TraitId(x) => x.lookup(db).container,
+ TypeOwnerId::TraitAliasId(x) => x.lookup(db).container,
+ TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db),
+ TypeOwnerId::ImplId(x) => x.lookup(db).container,
+ TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container,
+ TypeOwnerId::ModuleId(x) => *x,
}
}
}
-impl DefWithBodyId {
- pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
+impl HasModule for DefWithBodyId {
+ fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
+ DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
+ DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
+ DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
+ DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
}
}
}
@@ -799,52 +1058,43 @@ impl AttrDefId {
pub trait AsMacroCall {
fn as_call_id(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> {
- self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
+ self.as_call_id_with_errors(db, krate, resolver).ok()?.value
}
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
}
impl AsMacroCall for InFile<&ast::MacroCall> {
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- mut error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
- let h = Hygiene::new(db.upcast(), self.file_id);
- let path =
- self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
-
- let path = match error_sink
- .option(path, || ExpandError::Other("malformed macro invocation".into()))
- {
- Ok(path) => path,
- Err(error) => {
- return Ok(Err(error));
- }
+ let h = Hygiene::new(db, self.file_id);
+ let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
+
+ let Some(path) = path else {
+ return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
};
- macro_call_as_call_id(
+ macro_call_as_call_id_(
db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
expands_to,
krate,
resolver,
- error_sink,
)
}
}
@@ -863,26 +1113,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
}
fn macro_call_as_call_id(
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
call: &AstIdWithPath<ast::MacroCall>,
expand_to: ExpandTo,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<Option<MacroCallId>, UnresolvedMacro> {
+ macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
+}
+
+fn macro_call_as_call_id_(
+ db: &dyn ExpandDatabase,
+ call: &AstIdWithPath<ast::MacroCall>,
+ expand_to: ExpandTo,
+ krate: CrateId,
+ resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let def =
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
- let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
-
- expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
+ let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
+ expand_eager_macro_input(db, krate, macro_call, def, &resolver)?
} else {
- Ok(def.as_lazy_macro(
- db.upcast(),
- krate,
- MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
- ))
+ ExpandResult {
+ value: Some(def.as_lazy_macro(
+ db,
+ krate,
+ MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
+ )),
+ err: None,
+ }
};
Ok(res)
}
@@ -986,16 +1247,15 @@ fn attr_macro_as_call_id(
macro_attr: &Attr,
krate: CrateId,
def: MacroDefId,
- is_derive: bool,
) -> MacroCallId {
let arg = match macro_attr.input.as_deref() {
- Some(AttrInput::TokenTree(tt, map)) => (
+ Some(AttrInput::TokenTree(tt)) => (
{
- let mut tt = tt.clone();
+ let mut tt = tt.0.clone();
tt.delimiter = tt::Delimiter::UNSPECIFIED;
tt
},
- map.clone(),
+ tt.1.clone(),
),
_ => (tt::Subtree::empty(), Default::default()),
};
@@ -1007,7 +1267,6 @@ fn attr_macro_as_call_id(
ast_id: item_attr.ast_id,
attr_args: Arc::new(arg),
invoc_attr_index: macro_attr.id,
- is_derive,
},
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
new file mode 100644
index 000000000..af623fd0e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -0,0 +1,45 @@
+//! Context for lowering paths.
+use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
+use once_cell::unsync::OnceCell;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{db::DefDatabase, path::Path};
+
+pub struct LowerCtx<'a> {
+ pub db: &'a dyn DefDatabase,
+ hygiene: Hygiene,
+ ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
+}
+
+impl<'a> LowerCtx<'a> {
+ pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
+ }
+
+ pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+ LowerCtx {
+ db,
+ hygiene: Hygiene::new(db.upcast(), file_id),
+ ast_id_map: Some((file_id, OnceCell::new())),
+ }
+ }
+
+ pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
+ }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+
+ pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
+ Path::from_src(ast, self)
+ }
+
+ pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
+ let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
+ let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
+ Some(InFile::new(file_id, ast_id_map.ast_id(item)))
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index fafcde25a..f41f97190 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl < > core::marker::Copy for Foo< > {}"#]],
+impl < > core::marker::Copy for Foo< > where {}"#]],
);
}
@@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
-impl < > crate ::marker::Copy for Foo< > {}"#]],
+impl < > crate ::marker::Copy for Foo< > where {}"#]],
);
}
@@ -57,7 +57,7 @@ struct Foo<A, B>;
#[derive(Copy)]
struct Foo<A, B>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -84,13 +84,93 @@ fn test_clone_expand() {
r#"
//- minicore: derive, clone
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
"#,
expect![[r#"
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
+
+impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>Command::Move {
+ x: x.clone(), y: y.clone(),
+ }
+ , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_clone_expand_with_associated_types() {
+ check(
+ r#"
+//- minicore: derive, clone
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
+"#,
+ expect![[r#"
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
-impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo {
+ qualified: qualified, shorthand: shorthand, generic: generic,
+ }
+ =>Foo {
+ qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(),
+ }
+ ,
+ }
+ }
+}"#]],
);
}
@@ -106,6 +186,270 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
-impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo(f0, )=>Foo(f0.clone(), ),
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_default_expand() {
+ check(
+ r#"
+//- minicore: derive, default
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+"#,
+ expect![[r#"
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+
+impl < > core::default::Default for Foo< > where {
+ fn default() -> Self {
+ Foo {
+ field1: core::default::Default::default(), field2: core::default::Default::default(),
+ }
+ }
+}
+impl < > core::default::Default for Bar< > where {
+ fn default() -> Self {
+ Bar::Bar
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_partial_eq_expand() {
+ check(
+ r#"
+//- minicore: derive, eq
+#[derive(PartialEq, Eq)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(PartialEq, Eq)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::cmp::PartialEq for Command< > where {
+ fn eq(&self , other: &Self ) -> bool {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
+ }
+ }
+}
+impl < > core::cmp::Eq for Command< > where {}"#]],
+ );
+}
+
+#[test]
+fn test_partial_ord_expand() {
+ check(
+ r#"
+//- minicore: derive, ord
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::cmp::PartialOrd for Command< > where {
+ fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
+ match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.partial_cmp(&x_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match y_self.partial_cmp(&y_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ }
+ c=>return c,
+ }
+ }
+}
+impl < > core::cmp::Ord for Command< > where {
+ fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
+ match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
+ core::cmp::Ordering::Equal=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.cmp(&x_other) {
+ core::cmp::Ordering::Equal=> {
+ match y_self.cmp(&y_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
+ }
+ }
+ c=>return c,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_hash_expand() {
+ check(
+ r#"
+//- minicore: derive, hash
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::hash::Hash for Command< > where {
+ fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) {
+ core::mem::discriminant(self ).hash(ra_expand_state);
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ => {
+ x.hash(ra_expand_state);
+ y.hash(ra_expand_state);
+ }
+ , Command::Do(f0, )=> {
+ f0.hash(ra_expand_state);
+ }
+ , Command::Jump=> {}
+ ,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_debug_expand() {
+ check(
+ r#"
+//- minicore: derive, fmt
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::fmt::Debug for Command< > where {
+ fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"),
+ }
+ }
+}"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 5fbd1789b..07d9baa58 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -13,12 +13,12 @@ macro_rules! column {() => {}}
fn main() { column!(); }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! column {() => {}}
-fn main() { 0; }
-"##]],
+fn main() { 0 as u32; }
+"#]],
);
}
@@ -31,12 +31,12 @@ macro_rules! line {() => {}}
fn main() { line!() }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! line {() => {}}
-fn main() { 0 }
-"##]],
+fn main() { 0 as u32 }
+"#]],
);
}
@@ -79,7 +79,7 @@ fn main() { env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! env {() => {}}
-fn main() { "__RA_UNIMPLEMENTED__"; }
+fn main() { "UNRESOLVED_ENV_VAR"; }
"##]],
);
}
@@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! option_env {() => {}}
-fn main() { $crate::option::Option::None:: < &str>; }
+fn main() { ::core::option::Option::None:: < &str>; }
"#]],
);
}
@@ -193,7 +193,7 @@ fn main() {
format_args!("{} {:?}", arg1(a, b, c), arg2);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -201,9 +201,47 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]);
+ ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn regression_15002() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(x = 2);
+ format_args!(x =);
+ format_args!(x =, x = 2);
+ format_args!("{}", x =);
+ format_args!(=, "{}", x =);
+ format_args!(x = 2, "{}", 5);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(), ::core::fmt::Display::fmt), ]);
+ /* error: no rule matches input tokens */;
+ ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(5), ::core::fmt::Display::fmt), ]);
+}
+"##]],
);
}
@@ -221,7 +259,7 @@ fn main() {
format_args!("{} {:?}", a::<A,B>(), b);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -229,9 +267,76 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]);
+ ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_with_raw_strings() {
+ check(
+ r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(
+ r#"{},mismatch,"{}","{}""#,
+ location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ mismatch.expected.display(db),
+ mismatch.actual.display(db)
+ );
+}
+"##,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
+}
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_eager() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
+}
+"##]],
);
}
@@ -250,7 +355,7 @@ fn main() {
format_args!/*+errors*/("{} {:?}", a.);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -259,10 +364,10 @@ macro_rules! format_args {
fn main() {
let _ =
- /* parse error: expected field name or number */
-$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]);
+ /* error: no rule matches input tokens *//* parse error: expected field name or number */
+::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
);
}
@@ -337,10 +442,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { concat!(surprise!()); }
"##,
expect![[r##"
@@ -351,10 +452,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { "s"; }
"##]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 7a3e8c3b0..553ffe3d0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -4,6 +4,7 @@
mod tt_conversion;
mod matching;
mod meta_syntax;
+mod metavar_expr;
mod regression;
use expect_test::expect;
@@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
);
}
#[test]
-fn float_field_acces_macro_input() {
+fn float_field_access_macro_input() {
check(
r#"
macro_rules! foo {
@@ -922,7 +923,7 @@ macro_rules! m {
fn bar() -> &'a Baz<u8> {}
-fn bar() -> extern "Rust"fn() -> Ret {}
+fn bar() -> extern "Rust" fn() -> Ret {}
"#]],
);
}
@@ -1293,20 +1294,54 @@ ok!();
}
#[test]
-fn test_vertical_bar_with_pat() {
+fn test_vertical_bar_with_pat_param() {
check(
r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
m! { |x| }
"#,
expect![[r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
ok!();
"#]],
);
}
#[test]
+fn test_new_std_matches() {
+ check(
+ r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ matches!(0, 0 | 1 if true);
+}
+ "#,
+ expect![[r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ match 0 {
+ 0|1 if true =>true , _=>false
+ };
+}
+ "#]],
+ );
+}
+
+#[test]
fn test_dollar_crate_lhs_is_not_meta() {
check(
r#"
@@ -1581,92 +1616,6 @@ struct Foo;
}
#[test]
-fn test_dollar_dollar() {
- check(
- r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-register_struct!(Foo);
-register_methods!(alpha, beta);
-implement_methods!(1, 2, 3);
-"#,
- expect![[r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-macro_rules !register_methods {
- ($($method: ident), *) = > {
- macro_rules!implement_methods {
- ($$($val: expr), *) = > {
- struct Foo;
- impl Foo {
- $(fn $method()-> &'static[u32] {
- &[$$($$val), *]
- }
- )*
- }
- }
- }
- }
-}
-macro_rules !implement_methods {
- ($($val: expr), *) = > {
- struct Foo;
- impl Foo {
- fn alpha()-> &'static[u32] {
- &[$($val), *]
- }
- fn beta()-> &'static[u32] {
- &[$($val), *]
- }
- }
- }
-}
-struct Foo;
-impl Foo {
- fn alpha() -> &'static[u32] {
- &[1, 2, 3]
- }
- fn beta() -> &'static[u32] {
- &[1, 2, 3]
- }
-}
-"#]],
- )
-}
-
-#[test]
-fn test_metavar_exprs() {
- check(
- r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = m!(a b c);
- "#,
- expect![[r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = -0--1--2;
- "#]],
- );
-}
-
-#[test]
fn test_punct_without_space() {
// Puncts are "glued" greedily.
check(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 26f16542c..0909d8c83 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -33,7 +33,7 @@ m!(&k");
"#,
expect![[r#"
macro_rules! m { ($i:literal) => {}; }
-/* error: Failed to lower macro args to token tree */"#]],
+/* error: invalid token tree */"#]],
);
}
@@ -73,7 +73,7 @@ fn main() {
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
fn main() {
- let a = 2let b = 5drop(b-a)println!("{}", a+b)
+ let a = 2 let b = 5 drop(b-a)println!("{}", a+b)
}
"#]],
)
@@ -106,7 +106,6 @@ stringify!(;
#[test]
fn range_patterns() {
- // FIXME: rustc thinks there are three patterns here, not one.
check(
r#"
macro_rules! m {
@@ -118,7 +117,7 @@ m!(.. .. ..);
macro_rules! m {
($($p:pat)*) => (stringify!($($p |)*);)
}
-stringify!(.. .. .. |);
+stringify!(.. | .. | .. |);
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
new file mode 100644
index 000000000..967b5ad36
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
@@ -0,0 +1,311 @@
+//! Tests for RFC 3086 metavariable expressions.
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_dollar_dollar() {
+ check(
+ r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+register_struct!(Foo);
+register_methods!(alpha, beta);
+implement_methods!(1, 2, 3);
+"#,
+ expect![[r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+macro_rules !register_methods {
+ ($($method: ident), *) = > {
+ macro_rules!implement_methods {
+ ($$($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ $(fn $method()-> &'static[u32] {
+ &[$$($$val), *]
+ }
+ )*
+ }
+ }
+ }
+ }
+}
+macro_rules !implement_methods {
+ ($($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ fn alpha()-> &'static[u32] {
+ &[$($val), *]
+ }
+ fn beta()-> &'static[u32] {
+ &[$($val), *]
+ }
+ }
+ }
+}
+struct Foo;
+impl Foo {
+ fn alpha() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+ fn beta() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+}
+"#]],
+ )
+}
+
+#[test]
+fn test_metavar_exprs() {
+ check(
+ r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = m!(a b c);
+ "#,
+ expect![[r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = -0--1--2;
+ "#]],
+ );
+}
+
+#[test]
+fn count_basic() {
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ m!();
+ m!(a);
+ m!(a, a);
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ 0;
+ 1;
+ 2;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_with_depth() {
+ check(
+ r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ foo!(
+ a a a, a, a a;
+ a a a
+ )
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ {
+ let depth_none = 6;
+ let depth_zero = 3;
+ let depth_one = 6;
+ } {
+ let depth_none = 3;
+ let depth_zero = 1;
+ let depth_one = 3;
+ }
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_depth_out_of_bounds() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ foo!(a b);
+ foo!(1 2; 3);
+ bar!(a b);
+ bar!(1 2; 3);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn misplaced_count() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ foo!(a b c);
+ foo!(1);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ /* error: ${count} misplaced */;
+ /* error: ${count} misplaced */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn malformed_count() {
+ check(
+ r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ too_many_args!();
+ depth_suffixed!();
+ depth_too_large!();
+}
+"#,
+ expect![[r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_interaction_with_empty_binding() {
+ // FIXME: Should this error? rustc currently accepts it.
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ m!();
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ 0;
+}
+"#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index b663a2917..d8e4a4dcc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst {
#[derive(Clone)] struct CharEscapeDebugContinue;
impl Fn<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
c.escape_debug_ext(false )
}
}
}
impl FnMut<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDebugContinue {
type Output = char::EscapeDebug;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeUnicode;
impl Fn<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
c.escape_unicode()
}
}
}
impl FnMut<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeUnicode {
type Output = char::EscapeUnicode;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeDefault;
impl Fn<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
c.escape_default()
}
}
}
impl FnMut<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDefault {
type Output = char::EscapeDefault;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&self , (c, ))
}
}
@@ -833,7 +833,7 @@ macro_rules! rgb_color {
/* parse error: expected SEMICOLON */
/* parse error: expected expression, item or let statement */
pub fn new() {
- let _ = 0as u32<<(8+8);
+ let _ = 0 as u32<<(8+8);
}
// MACRO_ITEMS@0..31
// FN@0..31
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index b8d2ca687..ae56934f6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} }
/* error: invalid macro definition: expected subtree */
-/* error: Failed to lower macro args to token tree */
+/* error: invalid token tree */
"#]],
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 314bf22b9..4a62696df 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -14,7 +14,7 @@ mod builtin_fn_macro;
mod builtin_derive_macro;
mod proc_macros;
-use std::{iter, ops::Range, sync::Arc};
+use std::{iter, ops::Range, sync};
use ::mbe::TokenMap;
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
@@ -33,8 +33,13 @@ use syntax::{
use tt::token_id::{Subtree, TokenId};
use crate::{
- db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
- src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId,
+ db::DefDatabase,
+ macro_id_to_def_id,
+ nameres::{DefMap, MacroSubNs, ModuleSource},
+ resolver::HasResolver,
+ src::HasSource,
+ test_db::TestDB,
+ AdtId, AsMacroCall, Lookup, ModuleDefId,
};
#[track_caller]
@@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
ProcMacro {
name: "identity_when_valid".into(),
kind: base_db::ProcMacroKind::Attr,
- expander: Arc::new(IdentityWhenValidProcMacroExpander),
+ expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
},
)];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
- let local_id = def_map.root();
+ let local_id = DefMap::ROOT;
let module = def_map.module_id(local_id);
let resolver = module.resolver(&db);
let source = def_map[local_id].definition_source(&db);
@@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, &macro_call);
- let mut error = None;
- let macro_call_id = macro_call
- .as_call_id_with_errors(
- &db,
- krate,
- |path| {
- resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
- },
- &mut |err| error = Some(err),
- )
- .unwrap()
+ let res = macro_call
+ .as_call_id_with_errors(&db, krate, |path| {
+ resolver
+ .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
+ .map(|it| macro_id_to_def_id(&db, it))
+ })
.unwrap();
+ let macro_call_id = res.value.unwrap();
let macro_file = MacroFile { macro_call_id };
let mut expansion_result = db.parse_macro_expansion(macro_file);
- expansion_result.err = expansion_result.err.or(error);
+ expansion_result.err = expansion_result.err.or(res.err);
expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
}
@@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
if let Some(err) = exp.err {
format_to!(expn_text, "/* error: {} */", err);
}
- if let Some((parse, token_map)) = exp.value {
- if expect_errors {
- assert!(!parse.errors().is_empty(), "no parse errors in expansion");
- for e in parse.errors() {
- format_to!(expn_text, "/* parse error: {} */\n", e);
- }
- } else {
- assert!(
- parse.errors().is_empty(),
- "parse errors in expansion: \n{:#?}",
- parse.errors()
- );
+ let (parse, token_map) = exp.value;
+ if expect_errors {
+ assert!(!parse.errors().is_empty(), "no parse errors in expansion");
+ for e in parse.errors() {
+ format_to!(expn_text, "/* parse error: {} */\n", e);
}
- let pp = pretty_print_macro_expansion(
- parse.syntax_node(),
- show_token_ids.then_some(&*token_map),
+ } else {
+ assert!(
+ parse.errors().is_empty(),
+ "parse errors in expansion: \n{:#?}",
+ parse.errors()
);
- let indent = IndentLevel::from_node(call.syntax());
- let pp = reindent(indent, pp);
- format_to!(expn_text, "{}", pp);
+ }
+ let pp = pretty_print_macro_expansion(
+ parse.syntax_node(),
+ show_token_ids.then_some(&*token_map),
+ );
+ let indent = IndentLevel::from_node(call.syntax());
+ let pp = reindent(indent, pp);
+ format_to!(expn_text, "{}", pp);
- if tree {
- let tree = format!("{:#?}", parse.syntax_node())
- .split_inclusive('\n')
- .map(|line| format!("// {line}"))
- .collect::<String>();
- format_to!(expn_text, "\n{}", tree)
- }
+ if tree {
+ let tree = format!("{:#?}", parse.syntax_node())
+ .split_inclusive('\n')
+ .map(|line| format!("// {line}"))
+ .collect::<String>();
+ format_to!(expn_text, "\n{}", tree)
}
let range = call.syntax().text_range();
let range: Range<usize> = range.into();
@@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
let curr_kind = token.kind();
let space = match (prev_kind, curr_kind) {
_ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
+ _ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
(T!['{'], T!['}']) => "",
(T![=], _) | (_, T![=]) => " ",
(_, T!['{']) => " ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 4efe8c58a..0ab1bd849 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -57,9 +57,9 @@ mod path_resolution;
#[cfg(test)]
mod tests;
-use std::{cmp::Ord, ops::Deref, sync::Arc};
+use std::{cmp::Ord, ops::Deref};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Edition, FileId, ProcMacroKind};
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
use itertools::Itertools;
use la_arena::Arena;
@@ -67,6 +67,7 @@ use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::format_to;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -76,7 +77,8 @@ use crate::{
path::ModPath,
per_ns::PerNs,
visibility::Visibility,
- AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
+ AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
+ MacroId, ModuleId, ProcMacroId,
};
/// Contains the results of (early) name resolution.
@@ -91,8 +93,10 @@ use crate::{
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
_c: Count<Self>,
+ /// When this is a block def map, this will hold the block id of the the block and module that
+ /// contains this block.
block: Option<BlockInfo>,
- root: LocalModuleId,
+ /// The modules and their data declared in this crate.
modules: Arena<ModuleData>,
krate: CrateId,
/// The prelude module for this crate. This either comes from an import
@@ -102,17 +106,32 @@ pub struct DefMap {
/// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways).
prelude: Option<ModuleId>,
- /// The extern prelude is only populated for non-block DefMaps
- extern_prelude: FxHashMap<Name, ModuleId>,
+ /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
+ /// this contains all kinds of macro, not just `macro_rules!` macro.
+ macro_use_prelude: FxHashMap<Name, MacroId>,
+
+ /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
+ /// attributes.
+ derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
+
+ /// The diagnostics that need to be emitted for this crate.
+ diagnostics: Vec<DefDiagnostic>,
+
+ /// The crate data that is shared between a crate's def map and all its block def maps.
+ data: Arc<DefMapCrateData>,
+}
+
+/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct DefMapCrateData {
+ /// The extern prelude which contains all root modules of external crates that are in scope.
+ extern_prelude: FxHashMap<Name, CrateRootModuleId>,
/// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
/// The error that occurred when failing to load the proc-macro dll.
proc_macro_loading_error: Option<Box<str>>,
- /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
- /// attributes.
- derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
/// Custom attributes registered with `#![register_attr]`.
registered_attrs: Vec<SmolStr>,
@@ -122,10 +141,36 @@ pub struct DefMap {
unstable_features: FxHashSet<SmolStr>,
/// #[rustc_coherence_is_core]
rustc_coherence_is_core: bool,
+ no_core: bool,
+ no_std: bool,
edition: Edition,
recursion_limit: Option<u32>,
- diagnostics: Vec<DefDiagnostic>,
+}
+
+impl DefMapCrateData {
+ fn shrink_to_fit(&mut self) {
+ let Self {
+ extern_prelude,
+ exported_derives,
+ fn_proc_macro_mapping,
+ registered_attrs,
+ registered_tools,
+ unstable_features,
+ proc_macro_loading_error: _,
+ rustc_coherence_is_core: _,
+ no_core: _,
+ no_std: _,
+ edition: _,
+ recursion_limit: _,
+ } = self;
+ extern_prelude.shrink_to_fit();
+ exported_derives.shrink_to_fit();
+ fn_proc_macro_mapping.shrink_to_fit();
+ registered_attrs.shrink_to_fit();
+ registered_tools.shrink_to_fit();
+ unstable_features.shrink_to_fit();
+ }
}
/// For `DefMap`s computed for a block expression, this stores its location in the parent map.
@@ -134,7 +179,23 @@ struct BlockInfo {
/// The `BlockId` this `DefMap` was created from.
block: BlockId,
/// The containing module.
- parent: ModuleId,
+ parent: BlockRelativeModuleId,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+struct BlockRelativeModuleId {
+ block: Option<BlockId>,
+ local_id: LocalModuleId,
+}
+
+impl BlockRelativeModuleId {
+ fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
+ self.into_module(krate).def_map(db)
+ }
+
+ fn into_module(self, krate: CrateId) -> ModuleId {
+ ModuleId { krate, block: self.block, local_id: self.local_id }
+ }
}
impl std::ops::Index<LocalModuleId> for DefMap {
@@ -224,6 +285,9 @@ pub struct ModuleData {
}
impl DefMap {
+ /// The module id of a crate or block root.
+ pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
+
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
@@ -243,17 +307,10 @@ impl DefMap {
Arc::new(def_map)
}
- pub(crate) fn block_def_map_query(
- db: &dyn DefDatabase,
- block_id: BlockId,
- ) -> Option<Arc<DefMap>> {
+ pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
let block: BlockLoc = db.lookup_intern_block(block_id);
let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
- let item_tree = tree_id.item_tree(db);
- if item_tree.top_level_items().is_empty() {
- return None;
- }
let parent_map = block.module.def_map(db);
let krate = block.module.krate;
@@ -265,36 +322,48 @@ impl DefMap {
let module_data =
ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
- let mut def_map = DefMap::empty(krate, parent_map.edition, module_data);
- def_map.block = Some(BlockInfo { block: block_id, parent: block.module });
+ let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
+ def_map.data = parent_map.data.clone();
+ def_map.block = Some(BlockInfo {
+ block: block_id,
+ parent: BlockRelativeModuleId {
+ block: block.module.block,
+ local_id: block.module.local_id,
+ },
+ });
let def_map = collector::collect_defs(db, def_map, tree_id);
- Some(Arc::new(def_map))
+ Arc::new(def_map)
}
fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(module_data);
+ assert_eq!(root, Self::ROOT);
DefMap {
_c: Count::new(),
block: None,
+ modules,
krate,
- edition,
- recursion_limit: None,
- extern_prelude: FxHashMap::default(),
- exported_derives: FxHashMap::default(),
- fn_proc_macro_mapping: FxHashMap::default(),
- proc_macro_loading_error: None,
- derive_helpers_in_scope: FxHashMap::default(),
prelude: None,
- root,
- modules,
- registered_attrs: Vec::new(),
- registered_tools: Vec::new(),
- unstable_features: FxHashSet::default(),
+ macro_use_prelude: FxHashMap::default(),
+ derive_helpers_in_scope: FxHashMap::default(),
diagnostics: Vec::new(),
- rustc_coherence_is_core: false,
+ data: Arc::new(DefMapCrateData {
+ extern_prelude: FxHashMap::default(),
+ exported_derives: FxHashMap::default(),
+ fn_proc_macro_mapping: FxHashMap::default(),
+ proc_macro_loading_error: None,
+ registered_attrs: Vec::new(),
+ registered_tools: Vec::new(),
+ unstable_features: FxHashSet::default(),
+ rustc_coherence_is_core: false,
+ no_core: false,
+ no_std: false,
+ edition,
+ recursion_limit: None,
+ }),
}
}
@@ -317,31 +386,31 @@ impl DefMap {
}
pub fn registered_tools(&self) -> &[SmolStr] {
- &self.registered_tools
+ &self.data.registered_tools
}
pub fn registered_attrs(&self) -> &[SmolStr] {
- &self.registered_attrs
+ &self.data.registered_attrs
}
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
- self.unstable_features.contains(feature)
+ self.data.unstable_features.contains(feature)
}
pub fn is_rustc_coherence_is_core(&self) -> bool {
- self.rustc_coherence_is_core
+ self.data.rustc_coherence_is_core
}
- pub fn root(&self) -> LocalModuleId {
- self.root
+ pub fn is_no_std(&self) -> bool {
+ self.data.no_std || self.data.no_core
}
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
- self.fn_proc_macro_mapping.get(&id).copied()
+ self.data.fn_proc_macro_mapping.get(&id).copied()
}
pub fn proc_macro_loading_error(&self) -> Option<&str> {
- self.proc_macro_loading_error.as_deref()
+ self.data.proc_macro_loading_error.as_deref()
}
pub fn krate(&self) -> CrateId {
@@ -356,8 +425,12 @@ impl DefMap {
self.prelude
}
- pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
- self.extern_prelude.iter()
+ pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
+ self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+ }
+
+ pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+ self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
}
pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@@ -365,11 +438,8 @@ impl DefMap {
ModuleId { krate: self.krate, local_id, block }
}
- pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId {
- self.with_ancestor_maps(db, self.root, &mut |def_map, _module| {
- if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
- })
- .expect("DefMap chain without root")
+ pub fn crate_root(&self) -> CrateRootModuleId {
+ CrateRootModuleId { krate: self.krate }
}
pub(crate) fn resolve_path(
@@ -378,9 +448,16 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> (PerNs, Option<usize>) {
- let res =
- self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
+ let res = self.resolve_path_fp_with_macro(
+ db,
+ ResolveMode::Other,
+ original_module,
+ path,
+ shadow,
+ expected_macro_subns,
+ );
(res.resolved_def, res.segment_index)
}
@@ -397,6 +474,7 @@ impl DefMap {
original_module,
path,
shadow,
+ None, // Currently this function isn't used for macro resolution.
);
(res.resolved_def, res.segment_index)
}
@@ -405,7 +483,7 @@ impl DefMap {
///
/// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
/// `None`, iteration continues.
- pub fn with_ancestor_maps<T>(
+ pub(crate) fn with_ancestor_maps<T>(
&self,
db: &dyn DefDatabase,
local_mod: LocalModuleId,
@@ -416,7 +494,7 @@ impl DefMap {
}
let mut block = self.block;
while let Some(block_info) = block {
- let parent = block_info.parent.def_map(db);
+ let parent = block_info.parent.def_map(db, self.krate);
if let Some(it) = f(&parent, block_info.parent.local_id) {
return Some(it);
}
@@ -429,7 +507,8 @@ impl DefMap {
/// If this `DefMap` is for a block expression, returns the module containing the block (which
/// might again be a block, or a module inside a block).
pub fn parent(&self) -> Option<ModuleId> {
- Some(self.block?.parent)
+ let BlockRelativeModuleId { block, local_id } = self.block?.parent;
+ Some(ModuleId { krate: self.krate, block, local_id })
}
/// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
@@ -437,7 +516,13 @@ impl DefMap {
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
match self[local_mod].parent {
Some(parent) => Some(self.module_id(parent)),
- None => self.block.map(|block| block.parent),
+ None => {
+ self.block.map(
+ |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| {
+ ModuleId { krate: self.krate, block, local_id }
+ },
+ )
+ }
}
}
@@ -448,25 +533,31 @@ impl DefMap {
let mut arc;
let mut current_map = self;
while let Some(block) = current_map.block {
- go(&mut buf, current_map, "block scope", current_map.root);
+ go(&mut buf, db, current_map, "block scope", Self::ROOT);
buf.push('\n');
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
- go(&mut buf, current_map, "crate", current_map.root);
+ go(&mut buf, db, current_map, "crate", Self::ROOT);
return buf;
- fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
+ fn go(
+ buf: &mut String,
+ db: &dyn DefDatabase,
+ map: &DefMap,
+ path: &str,
+ module: LocalModuleId,
+ ) {
format_to!(buf, "{}\n", path);
- map.modules[module].scope.dump(buf);
+ map.modules[module].scope.dump(db.upcast(), buf);
for (name, child) in
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
{
- let path = format!("{path}::{name}");
+ let path = format!("{path}::{}", name.display(db.upcast()));
buf.push('\n');
- go(buf, map, &path, *child);
+ go(buf, db, map, &path, *child);
}
}
}
@@ -477,7 +568,7 @@ impl DefMap {
let mut current_map = self;
while let Some(block) = current_map.block {
format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
@@ -489,34 +580,20 @@ impl DefMap {
// Exhaustive match to require handling new fields.
let Self {
_c: _,
- exported_derives,
- extern_prelude,
+ macro_use_prelude,
diagnostics,
modules,
- registered_attrs,
- registered_tools,
- fn_proc_macro_mapping,
derive_helpers_in_scope,
- unstable_features,
- proc_macro_loading_error: _,
block: _,
- edition: _,
- recursion_limit: _,
krate: _,
prelude: _,
- root: _,
- rustc_coherence_is_core: _,
+ data: _,
} = self;
- extern_prelude.shrink_to_fit();
- exported_derives.shrink_to_fit();
+ macro_use_prelude.shrink_to_fit();
diagnostics.shrink_to_fit();
modules.shrink_to_fit();
- registered_attrs.shrink_to_fit();
- registered_tools.shrink_to_fit();
- fn_proc_macro_mapping.shrink_to_fit();
derive_helpers_in_scope.shrink_to_fit();
- unstable_features.shrink_to_fit();
for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit();
module.scope.shrink_to_fit();
@@ -529,7 +606,7 @@ impl DefMap {
}
pub fn recursion_limit(&self) -> Option<u32> {
- self.recursion_limit
+ self.data.recursion_limit
}
}
@@ -564,3 +641,48 @@ pub enum ModuleSource {
Module(ast::Module),
BlockExpr(ast::BlockExpr),
}
+
+/// See `sub_namespace_match()`.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum MacroSubNs {
+ /// Function-like macros, suffixed with `!`.
+ Bang,
+ /// Macros inside attributes, i.e. attribute macros and derive macros.
+ Attr,
+}
+
+impl MacroSubNs {
+ fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self {
+ let expander = match macro_id {
+ MacroId::Macro2Id(it) => it.lookup(db).expander,
+ MacroId::MacroRulesId(it) => it.lookup(db).expander,
+ MacroId::ProcMacroId(it) => {
+ return match it.lookup(db).kind {
+ ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
+ ProcMacroKind::FuncLike => Self::Bang,
+ };
+ }
+ };
+
+ // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently.
+ match expander {
+ MacroExpander::Declarative
+ | MacroExpander::BuiltIn(_)
+ | MacroExpander::BuiltInEager(_) => Self::Bang,
+ MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr,
+ }
+ }
+}
+
+/// Quoted from [rustc]:
+/// Macro namespace is separated into two sub-namespaces, one for bang macros and
+/// one for attribute-like macros (attributes, derives).
+/// We ignore resolutions from one sub-namespace when searching names in scope for another.
+///
+/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75
+fn sub_namespace_match(candidate: Option<MacroSubNs>, expected: Option<MacroSubNs>) -> bool {
+ match (candidate, expected) {
+ (Some(candidate), Some(expected)) => candidate == expected,
+ _ => true,
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index 79cabeb0f..a7abf4459 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr};
use crate::{
- attr_macro_as_call_id, builtin_attr,
+ attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+ attr_macro_as_call_id,
db::DefDatabase,
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
@@ -13,7 +14,7 @@ use crate::{
AstIdWithPath, LocalModuleId, UnresolvedMacro,
};
-use super::DefMap;
+use super::{DefMap, MacroSubNs};
pub enum ResolvedAttr {
/// Attribute resolved to an attribute macro.
@@ -42,9 +43,12 @@ impl DefMap {
original_module,
&ast_id.path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Attr),
);
let def = match resolved_res.resolved_def.take_macros() {
Some(def) => {
+ // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
+ // macro, or even function-like macro when the path is qualified.
if def.is_attribute(db) {
def
} else {
@@ -60,7 +64,6 @@ impl DefMap {
attr,
self.krate,
macro_id_to_def_id(db, def),
- false,
)))
}
@@ -75,20 +78,16 @@ impl DefMap {
let name = name.to_smol_str();
let pred = |n: &_| *n == name;
- let registered = self.registered_tools.iter().map(SmolStr::as_str);
- let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+ let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
+ let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
// FIXME: tool modules can be shadowed by actual modules
if is_tool {
return true;
}
if segments.len() == 1 {
- let registered = self.registered_attrs.iter().map(SmolStr::as_str);
- let is_inert = builtin_attr::INERT_ATTRIBUTES
- .iter()
- .map(|it| it.name)
- .chain(registered)
- .any(pred);
+ let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
+ let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
return is_inert;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index ddcee77ec..62fb3c788 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -3,9 +3,9 @@
//! `DefCollector::collect` contains the fixed-point iteration loop which
//! resolves imports and expands macros.
-use std::{iter, mem};
+use std::{cmp::Ordering, iter, mem};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
@@ -14,10 +14,11 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
+ hygiene::Hygiene,
name::{name, AsName, Name},
proc_macro::ProcMacroExpander,
- ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
- MacroDefKind,
+ ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
+ MacroDefId, MacroDefKind,
};
use itertools::{izip, Itertools};
use la_arena::Idx;
@@ -25,6 +26,7 @@ use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -33,8 +35,8 @@ use crate::{
derive_macro_as_call_id,
item_scope::{ImportType, PerNsGlobImports},
item_tree::{
- self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
- MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
+ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
+ MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
},
macro_call_as_call_id, macro_id_to_def_id,
nameres::{
@@ -42,24 +44,25 @@ use crate::{
mod_resolution::ModDir,
path_resolution::ReachedFixedPoint,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
- BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
+ sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+ ResolveMode,
},
path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs,
tt,
visibility::{RawVisibility, Visibility},
- AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
- FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
- MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId,
- ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
- UnresolvedMacro,
+ AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
+ ExternBlockLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId,
+ Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId,
+ ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc,
+ TypeAliasLoc, UnionLoc, UnresolvedMacro,
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
let crate_graph = db.crate_graph();
let mut deps = FxHashMap::default();
@@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
let krate = &crate_graph[def_map.krate];
for dep in &krate.dependencies {
tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
- let dep_def_map = db.crate_def_map(dep.crate_id);
- let dep_root = dep_def_map.module_id(dep_def_map.root);
- deps.insert(dep.as_name(), dep_root);
-
- if dep.is_prelude() && !tree_id.is_block() {
- def_map.extern_prelude.insert(dep.as_name(), dep_root);
- }
+ deps.insert(dep.as_name(), dep.clone());
}
let cfg_options = &krate.cfg_options;
- let proc_macros = match &krate.proc_macro {
- Ok(proc_macros) => {
- proc_macros
- .iter()
- .enumerate()
- .map(|(idx, it)| {
- // FIXME: a hacky way to create a Name from string.
- let name =
- tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
- (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
- })
- .collect()
- }
- Err(e) => {
- def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
- Vec::new()
+
+ let is_proc_macro = krate.is_proc_macro;
+ let proc_macros = if is_proc_macro {
+ match db.proc_macros().get(&def_map.krate) {
+ Some(Ok(proc_macros)) => {
+ Ok(proc_macros
+ .iter()
+ .enumerate()
+ .map(|(idx, it)| {
+ // FIXME: a hacky way to create a Name from string.
+ let name =
+ tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
+ (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
+ })
+ .collect())
+ }
+ Some(Err(e)) => Err(e.clone().into_boxed_str()),
+ None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
}
+ } else {
+ Ok(vec![])
};
- let is_proc_macro = krate.is_proc_macro;
let mut collector = DefCollector {
db,
@@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro,
+ hygienes: FxHashMap::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -238,7 +239,7 @@ enum MacroDirectiveKind {
struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
- deps: FxHashMap<Name, ModuleId>,
+ deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
@@ -249,7 +250,7 @@ struct DefCollector<'a> {
/// built by the build system, and is the list of proc. macros we can actually expand. It is
/// empty when proc. macro support is disabled (in which case we still do name resolution for
/// them).
- proc_macros: Vec<(Name, ProcMacroExpander)>,
+ proc_macros: Result<Vec<(Name, ProcMacroExpander)>, Box<str>>,
is_proc_macro: bool,
from_glob_import: PerNsGlobImports,
/// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
@@ -259,6 +260,12 @@ struct DefCollector<'a> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general.
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+ /// `Hygiene` cache, because `Hygiene` construction is expensive.
+ ///
+ /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction.
+ /// However, `DefCollector` still needs to lower paths in attributes, in particular those in
+ /// derive meta item list.
+ hygienes: FxHashMap<HirFileId, Hygiene>,
}
impl DefCollector<'_> {
@@ -267,87 +274,113 @@ impl DefCollector<'_> {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.file_item_tree(file_id.into());
- let module_id = self.def_map.root;
-
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
- if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
- self.inject_prelude(&attrs);
-
- // Process other crate-level attributes.
- for attr in &*attrs {
- let attr_name = match attr.path.as_ident() {
- Some(name) => name,
- None => continue,
- };
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
- if *attr_name == hir_expand::name![recursion_limit] {
- if let Some(limit) = attr.string_value() {
- if let Ok(limit) = limit.parse() {
- self.def_map.recursion_limit = Some(limit);
- }
- }
- continue;
+ if let Err(e) = &self.proc_macros {
+ crate_data.proc_macro_loading_error = Some(e.clone());
+ }
+
+ for (name, dep) in &self.deps {
+ if dep.is_prelude() {
+ crate_data
+ .extern_prelude
+ .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+ }
+ }
+
+ // Process other crate-level attributes.
+ for attr in &*attrs {
+ if let Some(cfg) = attr.cfg() {
+ if self.cfg_options.check(&cfg) == Some(false) {
+ return;
}
+ }
+ let attr_name = match attr.path.as_ident() {
+ Some(name) => name,
+ None => continue,
+ };
- if *attr_name == hir_expand::name![crate_type] {
- if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
- self.is_proc_macro = true;
+ if *attr_name == hir_expand::name![recursion_limit] {
+ if let Some(limit) = attr.string_value() {
+ if let Ok(limit) = limit.parse() {
+ crate_data.recursion_limit = Some(limit);
}
- continue;
}
+ continue;
+ }
- if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
- self.def_map.rustc_coherence_is_core = true;
- continue;
+ if *attr_name == hir_expand::name![crate_type] {
+ if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
+ self.is_proc_macro = true;
}
+ continue;
+ }
- if *attr_name == hir_expand::name![feature] {
- let features =
- attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
- |feat| match feat.segments() {
- [name] => Some(name.to_smol_str()),
- _ => None,
- },
- );
- self.def_map.unstable_features.extend(features);
- }
+ if *attr_name == hir_expand::name![no_core] {
+ crate_data.no_core = true;
+ continue;
+ }
- let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
- || *attr_name == hir_expand::name![register_tool];
- if !attr_is_register_like {
- continue;
- }
+ if *attr_name == hir_expand::name![no_std] {
+ crate_data.no_std = true;
+ continue;
+ }
- let registered_name = match attr.single_ident_value() {
- Some(ident) => ident.as_name(),
- _ => continue,
- };
+ if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+ crate_data.rustc_coherence_is_core = true;
+ continue;
+ }
- if *attr_name == hir_expand::name![register_attr] {
- self.def_map.registered_attrs.push(registered_name.to_smol_str());
- cov_mark::hit!(register_attr);
- } else {
- self.def_map.registered_tools.push(registered_name.to_smol_str());
- cov_mark::hit!(register_tool);
- }
+ if *attr_name == hir_expand::name![feature] {
+ let hygiene = &Hygiene::new_unhygienic();
+ let features = attr
+ .parse_path_comma_token_tree(self.db.upcast(), hygiene)
+ .into_iter()
+ .flatten()
+ .filter_map(|feat| match feat.segments() {
+ [name] => Some(name.to_smol_str()),
+ _ => None,
+ });
+ crate_data.unstable_features.extend(features);
}
- ModCollector {
- def_collector: self,
- macro_depth: 0,
- module_id,
- tree_id: TreeId::new(file_id.into(), None),
- item_tree: &item_tree,
- mod_dir: ModDir::root(),
+ let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+ || *attr_name == hir_expand::name![register_tool];
+ if !attr_is_register_like {
+ continue;
}
- .collect_in_top_module(item_tree.top_level_items());
+
+ let registered_name = match attr.single_ident_value() {
+ Some(ident) => ident.as_name(),
+ _ => continue,
+ };
+
+ if *attr_name == hir_expand::name![register_attr] {
+ crate_data.registered_attrs.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_attr);
+ } else {
+ crate_data.registered_tools.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_tool);
+ }
+ }
+
+ crate_data.shrink_to_fit();
+ self.inject_prelude();
+
+ ModCollector {
+ def_collector: self,
+ macro_depth: 0,
+ module_id: DefMap::ROOT,
+ tree_id: TreeId::new(file_id.into(), None),
+ item_tree: &item_tree,
+ mod_dir: ModDir::root(),
}
+ .collect_in_top_module(item_tree.top_level_items());
}
fn seed_with_inner(&mut self, tree_id: TreeId) {
let item_tree = tree_id.item_tree(self.db);
- let module_id = self.def_map.root;
-
let is_cfg_enabled = item_tree
.top_level_attrs(self.db, self.def_map.krate)
.cfg()
@@ -356,7 +389,7 @@ impl DefCollector<'_> {
ModCollector {
def_collector: self,
macro_depth: 0,
- module_id,
+ module_id: DefMap::ROOT,
tree_id,
item_tree: &item_tree,
mod_dir: ModDir::root(),
@@ -428,7 +461,7 @@ impl DefCollector<'_> {
// Additionally, while the proc macro entry points must be `pub`, they are not publicly
// exported in type/value namespace. This function reduces the visibility of all items
// in the crate root that aren't proc macros.
- let root = self.def_map.root;
+ let root = DefMap::ROOT;
let module_id = self.def_map.module_id(root);
let root = &mut self.def_map.modules[root];
root.scope.censor_non_proc_macros(module_id);
@@ -456,12 +489,8 @@ impl DefCollector<'_> {
directive.module_id,
MacroCallKind::Attr {
ast_id: ast_id.ast_id,
- attr_args: std::sync::Arc::new((
- tt::Subtree::empty(),
- Default::default(),
- )),
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
invoc_attr_index: attr.id,
- is_derive: false,
},
attr.path().clone(),
));
@@ -495,15 +524,15 @@ impl DefCollector<'_> {
}
}
- fn inject_prelude(&mut self, crate_attrs: &Attrs) {
+ fn inject_prelude(&mut self) {
// See compiler/rustc_builtin_macros/src/standard_library_imports.rs
- if crate_attrs.by_key("no_core").exists() {
+ if self.def_map.data.no_core {
// libcore does not get a prelude.
return;
}
- let krate = if crate_attrs.by_key("no_std").exists() {
+ let krate = if self.def_map.data.no_std {
name![core]
} else {
let std = name![std];
@@ -516,43 +545,31 @@ impl DefCollector<'_> {
}
};
- let edition = match self.def_map.edition {
+ let edition = match self.def_map.data.edition {
Edition::Edition2015 => name![rust_2015],
Edition::Edition2018 => name![rust_2018],
Edition::Edition2021 => name![rust_2021],
};
- let path_kind = match self.def_map.edition {
+ let path_kind = match self.def_map.data.edition {
Edition::Edition2015 => PathKind::Plain,
_ => PathKind::Abs,
};
- let path =
- ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter());
- // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
- // FIXME remove this fallback
- let fallback_path =
- ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter());
-
- for path in &[path, fallback_path] {
- let (per_ns, _) = self.def_map.resolve_path(
- self.db,
- self.def_map.root,
- path,
- BuiltinShadowMode::Other,
- );
+ let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
- match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
- break;
- }
- types => {
- tracing::debug!(
- "could not resolve prelude path `{}` to module (resolved to {:?})",
- path,
- types
- );
- }
+ let (per_ns, _) =
+ self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
+
+ match per_ns.types {
+ Some((ModuleDefId::ModuleId(m), _)) => {
+ self.def_map.prelude = Some(m);
+ }
+ types => {
+ tracing::debug!(
+ "could not resolve prelude path `{}` to module (resolved to {:?})",
+ path.display(self.db.upcast()),
+ types
+ );
}
}
}
@@ -578,23 +595,28 @@ impl DefCollector<'_> {
def: ProcMacroDef,
id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId,
- module_id: ModuleId,
) {
+ if self.def_map.block.is_some() {
+ return;
+ }
let kind = def.kind.to_basedb_kind();
- let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
- Some(&(_, expander)) => (expander, kind),
- None => (ProcMacroExpander::dummy(), kind),
- };
+ let (expander, kind) =
+ match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
+ Ok(Some(&(_, expander))) => (expander, kind),
+ _ => (ProcMacroExpander::dummy(), kind),
+ };
let proc_macro_id =
- ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db);
+ ProcMacroLoc { container: self.def_map.crate_root(), id, expander, kind }
+ .intern(self.db);
self.define_proc_macro(def.name.clone(), proc_macro_id);
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
if let ProcMacroKind::CustomDerive { helpers } = def.kind {
- self.def_map
+ crate_data
.exported_derives
.insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers);
}
- self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
+ crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
}
/// Define a macro with `macro_rules`.
@@ -636,7 +658,7 @@ impl DefCollector<'_> {
// In Rust, `#[macro_export]` macros are unconditionally visible at the
// crate root, even if the parent modules is **not** visible.
if export {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -687,7 +709,7 @@ impl DefCollector<'_> {
/// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
/// And unconditionally exported.
fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -697,39 +719,28 @@ impl DefCollector<'_> {
);
}
- /// Import macros from `#[macro_use] extern crate`.
- fn import_macros_from_extern_crate(
- &mut self,
- current_module_id: LocalModuleId,
- extern_crate: &item_tree::ExternCrate,
- ) {
- tracing::debug!(
- "importing macros from extern crate: {:?} ({:?})",
- extern_crate,
- self.def_map.edition,
- );
-
- if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
- if m == self.def_map.module_id(current_module_id) {
- cov_mark::hit!(ignore_macro_use_extern_crate_self);
- return;
- }
-
- cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
- self.import_all_macros_exported(current_module_id, m.krate);
- }
- }
-
- /// Import all exported macros from another crate
+ /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
+ /// macros to be imported. Otherwise this method imports all exported macros.
///
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
+ fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
let def_map = self.db.crate_def_map(krate);
- for (name, def) in def_map[def_map.root].scope.macros() {
- // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
- self.define_legacy_macro(current_module_id, name.clone(), def);
+ // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
+ // macros.
+ let root_scope = &def_map[DefMap::ROOT].scope;
+ if let Some(names) = names {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, def);
+ }
+ }
+ } else {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), def);
+ }
}
}
@@ -762,8 +773,9 @@ impl DefCollector<'_> {
}
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
- let _p = profile::span("resolve_import").detail(|| format!("{}", import.path));
- tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
+ let _p = profile::span("resolve_import")
+ .detail(|| format!("{}", import.path.display(self.db.upcast())));
+ tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
if import.is_extern_crate {
let name = import
.path
@@ -785,6 +797,7 @@ impl DefCollector<'_> {
module_id,
&import.path,
BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
);
let def = res.resolved_def;
@@ -812,19 +825,12 @@ impl DefCollector<'_> {
}
}
- fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
+ fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
if *name == name!(self) {
cov_mark::hit!(extern_crate_self_as);
- let root = match self.def_map.block {
- Some(_) => {
- let def_map = self.def_map.crate_root(self.db).def_map(self.db);
- def_map.module_id(def_map.root())
- }
- None => self.def_map.module_id(self.def_map.root()),
- };
- Some(root)
+ Some(self.def_map.crate_root())
} else {
- self.deps.get(name).copied()
+ self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
}
}
@@ -863,11 +869,16 @@ impl DefCollector<'_> {
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if import.is_extern_crate
&& self.def_map.block.is_none()
- && module_id == self.def_map.root
+ && module_id == DefMap::ROOT
{
if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
{
- self.def_map.extern_prelude.insert(name.clone(), def);
+ if let Ok(def) = def.try_into() {
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), def);
+ }
}
}
@@ -1082,7 +1093,14 @@ impl DefCollector<'_> {
resolved.push((directive.module_id, directive.depth, directive.container, call_id));
};
let mut res = ReachedFixedPoint::Yes;
+ // Retain unresolved macros after this round of resolution.
macros.retain(|directive| {
+ let subns = match &directive.kind {
+ MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
+ MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
+ MacroSubNs::Attr
+ }
+ };
let resolver = |path| {
let resolved_res = self.def_map.resolve_path_fp_with_macro(
self.db,
@@ -1090,6 +1108,7 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(subns),
);
resolved_res
.resolved_def
@@ -1101,15 +1120,15 @@ impl DefCollector<'_> {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
let call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
resolver_def_id,
- &mut |_err| (),
);
- if let Ok(Ok(call_id)) = call_id {
+ if let Ok(Some(call_id)) = call_id {
push_resolved(directive, call_id);
+
res = ReachedFixedPoint::No;
return false;
}
@@ -1134,7 +1153,7 @@ impl DefCollector<'_> {
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = self.db.crate_def_map(def_id.krate);
- if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+ if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
self.def_map
.derive_helpers_in_scope
.entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1214,7 +1233,19 @@ impl DefCollector<'_> {
};
let ast_id = ast_id.with_value(ast_adt_id);
- match attr.parse_path_comma_token_tree() {
+ let extend_unhygenic;
+ let hygiene = if file_id.is_macro() {
+ self.hygienes
+ .entry(file_id)
+ .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id))
+ } else {
+ // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry
+ // when we're in an oridinary (non-macro) file.
+ extend_unhygenic = Hygiene::new_unhygienic();
+ &extend_unhygenic
+ };
+
+ match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
Some(derive_macros) => {
let mut len = 0;
for (idx, path) in derive_macros.enumerate() {
@@ -1241,7 +1272,6 @@ impl DefCollector<'_> {
attr,
self.def_map.krate,
def,
- true,
);
self.def_map.modules[directive.module_id]
.scope
@@ -1261,18 +1291,12 @@ impl DefCollector<'_> {
}
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
- let call_id = attr_macro_as_call_id(
- self.db,
- file_ast_id,
- attr,
- self.def_map.krate,
- def,
- false,
- );
+ let call_id =
+ attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
// If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
+ if !self.db.expand_proc_attr_macros() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
@@ -1345,25 +1369,31 @@ impl DefCollector<'_> {
let file_id = macro_call_id.as_file();
// First, fetch the raw expansion result for purposes of error reporting. This goes through
- // `macro_expand_error` to avoid depending on the full expansion result (to improve
+ // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
// incrementality).
- let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
- let err = self.db.macro_expand_error(macro_call_id);
+ let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
if let Some(err) = err {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = match err {
+ // why is this reported here?
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
always!(krate == loc.def.krate);
- // Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
}
- _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
+ _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
};
self.def_map.diagnostics.push(diag);
}
+ if let errors @ [_, ..] = &*value {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
+ let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
+ self.def_map.diagnostics.push(diag);
+ }
// Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id);
+
let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector {
def_collector: &mut *self,
@@ -1384,8 +1414,9 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
+ // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
let macro_call_as_call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
@@ -1396,13 +1427,13 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Bang),
);
resolved_res
.resolved_def
.take_macros()
.map(|it| macro_id_to_def_id(self.db, it))
},
- &mut |_| (),
);
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@@ -1489,6 +1520,7 @@ impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate;
+ let is_crate_root = self.module_id == DefMap::ROOT;
// Note: don't assert that inserted value is fresh: it's simply not true
// for macros.
@@ -1496,28 +1528,22 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`.
if let Some(prelude_module) = self.def_collector.def_map.prelude {
- if prelude_module.krate != krate {
+ if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
+ self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
}
}
// This should be processed eagerly instead of deferred to resolving.
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items.
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
- if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+ //
+ // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
+ // ignore them.
+ if is_crate_root {
+ for &item in items {
if let ModItem::ExternCrate(id) = item {
- let import = &self.item_tree[id];
- let attrs = self.item_tree.attrs(
- self.def_collector.db,
- krate,
- ModItem::from(id).into(),
- );
- if attrs.by_key("macro_use").exists() {
- self.def_collector.import_macros_from_extern_crate(self.module_id, import);
- }
+ self.process_macro_use_extern_crate(id);
}
}
}
@@ -1610,14 +1636,12 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
- if self.def_collector.is_proc_macro && self.module_id == def_map.root {
+ if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
- let crate_root = def_map.module_id(def_map.root);
self.def_collector.export_proc_macro(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
- crate_root,
);
}
}
@@ -1744,6 +1768,50 @@ impl ModCollector<'_, '_> {
}
}
+ fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ let db = self.def_collector.db;
+ let attrs = self.item_tree.attrs(
+ db,
+ self.def_collector.def_map.krate,
+ ModItem::from(extern_crate).into(),
+ );
+ if let Some(cfg) = attrs.cfg() {
+ if !self.is_cfg_enabled(&cfg) {
+ return;
+ }
+ }
+
+ let target_crate =
+ match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
+ Some(m) if m.krate == self.def_collector.def_map.krate => {
+ cov_mark::hit!(ignore_macro_use_extern_crate_self);
+ return;
+ }
+ Some(m) => m.krate,
+ None => return,
+ };
+
+ cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
+
+ let mut single_imports = Vec::new();
+ let hygiene = Hygiene::new_unhygienic();
+ for attr in attrs.by_key("macro_use").attrs() {
+ let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
+ // `#[macro_use]` (without any paths) found, forget collected names and just import
+ // all visible macros.
+ self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ return;
+ };
+ for path in paths {
+ if let Some(name) = path.as_ident() {
+ single_imports.push(name.clone());
+ }
+ }
+ }
+
+ self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ }
+
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value();
let is_macro_use = attrs.by_key("macro_use").exists();
@@ -1844,7 +1912,6 @@ impl ModCollector<'_, '_> {
let vis = def_map
.resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
.unwrap_or(Visibility::Public);
- let modules = &mut def_map.modules;
let origin = match definition {
None => ModuleOrigin::Inline {
definition: declaration,
@@ -1858,11 +1925,16 @@ impl ModCollector<'_, '_> {
},
};
+ let modules = &mut def_map.modules;
let res = modules.alloc(ModuleData::new(origin, vis));
modules[res].parent = Some(self.module_id);
- for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
- for &mac in &mac {
- modules[res].scope.define_legacy_macro(name.clone(), mac);
+
+ if let Some((target, source)) = Self::borrow_modules(modules.as_mut(), res, self.module_id)
+ {
+ for (name, macs) in source.scope.legacy_macros() {
+ for &mac in macs {
+ target.scope.define_legacy_macro(name.clone(), mac);
+ }
}
}
modules[self.module_id].children.insert(name.clone(), res);
@@ -1919,7 +1991,10 @@ impl ModCollector<'_, '_> {
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
continue;
}
- tracing::debug!("non-builtin attribute {}", attr.path);
+ tracing::debug!(
+ "non-builtin attribute {}",
+ attr.path.display(self.def_collector.db.upcast())
+ );
let ast_id = AstIdWithPath::new(
self.file_id(),
@@ -2053,8 +2128,8 @@ impl ModCollector<'_, '_> {
stdx::always!(
name == mac.name,
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
- mac.name,
- name
+ mac.name.display(self.def_collector.db.upcast()),
+ name.display(self.def_collector.db.upcast())
);
helpers_opt = Some(helpers);
}
@@ -2089,73 +2164,60 @@ impl ModCollector<'_, '_> {
&self.item_tree[mac.visibility],
);
if let Some(helpers) = helpers_opt {
- self.def_collector
- .def_map
- .exported_derives
- .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ if self.def_collector.def_map.block.is_none() {
+ Arc::get_mut(&mut self.def_collector.def_map.data)
+ .unwrap()
+ .exported_derives
+ .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ }
}
}
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+ let db = self.def_collector.db;
+
+ // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+ // new legacy macros that create textual scopes. We need a way to resolve names in textual
+ // scopes without eager expansion.
- // Case 1: try to resolve in legacy scope and expand macro_rules
- let mut error = None;
- match macro_call_as_call_id(
- self.def_collector.db,
+ // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
+ if let Ok(res) = macro_call_as_call_id(
+ db.upcast(),
&ast_id,
mac.expand_to,
self.def_collector.def_map.krate,
|path| {
path.as_ident().and_then(|name| {
- self.def_collector.def_map.with_ancestor_maps(
- self.def_collector.db,
- self.module_id,
- &mut |map, module| {
- map[module]
- .scope
- .get_legacy_macro(name)
- .and_then(|it| it.last())
- .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
- },
- )
+ let def_map = &self.def_collector.def_map;
+ def_map
+ .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+ map[module].scope.get_legacy_macro(name)?.last().copied()
+ })
+ .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
+ .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .filter(|&id| {
+ sub_namespace_match(
+ Some(MacroSubNs::from_id(db, id)),
+ Some(MacroSubNs::Bang),
+ )
+ })
+ .map(|it| macro_id_to_def_id(self.def_collector.db, it))
})
},
- &mut |err| {
- error.get_or_insert(err);
- },
) {
- Ok(Ok(macro_call_id)) => {
- // Legacy macros need to be expanded immediately, so that any macros they produce
- // are in scope.
+ // Legacy macros need to be expanded immediately, so that any macros they produce
+ // are in scope.
+ if let Some(val) = res {
self.def_collector.collect_macro_expansion(
self.module_id,
- macro_call_id,
+ val,
self.macro_depth + 1,
container,
);
-
- if let Some(err) = error {
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- err.to_string(),
- ));
- }
-
- return;
}
- Ok(Err(_)) => {
- // Built-in macro failed eager expansion.
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- error.unwrap().to_string(),
- ));
- return;
- }
- Err(UnresolvedMacro { .. }) => (),
+ return;
}
// Case 2: resolve in module scope, expand during name resolution.
@@ -2168,14 +2230,40 @@ impl ModCollector<'_, '_> {
}
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
- let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
- for (name, macs) in macros {
+ let Some((source, target)) = Self::borrow_modules(self.def_collector.def_map.modules.as_mut(), module_id, self.module_id) else {
+ return
+ };
+
+ for (name, macs) in source.scope.legacy_macros() {
macs.last().map(|&mac| {
- self.def_collector.define_legacy_macro(self.module_id, name.clone(), mac)
+ target.scope.define_legacy_macro(name.clone(), mac);
});
}
}
+ /// Mutably borrow two modules at once, retu
+ fn borrow_modules(
+ modules: &mut [ModuleData],
+ a: LocalModuleId,
+ b: LocalModuleId,
+ ) -> Option<(&mut ModuleData, &mut ModuleData)> {
+ let a = a.into_raw().into_u32() as usize;
+ let b = b.into_raw().into_u32() as usize;
+
+ let (a, b) = match a.cmp(&b) {
+ Ordering::Equal => return None,
+ Ordering::Less => {
+ let (prefix, b) = modules.split_at_mut(b);
+ (&mut prefix[a], &mut b[0])
+ }
+ Ordering::Greater => {
+ let (prefix, a) = modules.split_at_mut(a);
+ (&mut a[0], &mut prefix[b])
+ }
+ };
+ Some((a, b))
+ }
+
fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
self.def_collector.cfg_options.check(cfg) != Some(false)
}
@@ -2215,10 +2303,11 @@ mod tests {
unresolved_macros: Vec::new(),
mod_dirs: FxHashMap::default(),
cfg_options: &CfgOptions::default(),
- proc_macros: Default::default(),
+ proc_macros: Ok(vec![]),
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro: false,
+ hygienes: FxHashMap::default(),
};
collector.seed_with_top_level();
collector.collect();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index b024d7c67..18b424255 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,7 +4,10 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use hir_expand::{attrs::AttrId, MacroCallKind};
use la_arena::Idx;
-use syntax::ast::{self, AnyHasAttrs};
+use syntax::{
+ ast::{self, AnyHasAttrs},
+ SyntaxError,
+};
use crate::{
item_tree::{self, ItemTreeId},
@@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
MacroError { ast: MacroCallKind, message: String },
+ MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
+
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
+
+ MacroDefError { ast: AstId<ast::Macro>, message: String },
}
#[derive(Debug, PartialEq, Eq)]
@@ -81,7 +88,8 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
}
- pub(super) fn unresolved_proc_macro(
+ // FIXME: Whats the difference between this and unresolved_macro_call
+ pub(crate) fn unresolved_proc_macro(
container: LocalModuleId,
ast: MacroCallKind,
krate: CrateId,
@@ -89,7 +97,7 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
}
- pub(super) fn macro_error(
+ pub(crate) fn macro_error(
container: LocalModuleId,
ast: MacroCallKind,
message: String,
@@ -97,7 +105,22 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
}
- pub(super) fn unresolved_macro_call(
+ pub(crate) fn macro_expansion_parse_error(
+ container: LocalModuleId,
+ ast: MacroCallKind,
+ errors: &[SyntaxError],
+ ) -> Self {
+ Self {
+ in_module: container,
+ kind: DefDiagnosticKind::MacroExpansionParseError {
+ ast,
+ errors: errors.to_vec().into_boxed_slice(),
+ },
+ }
+ }
+
+ // FIXME: Whats the difference between this and unresolved_proc_macro
+ pub(crate) fn unresolved_macro_call(
container: LocalModuleId,
ast: MacroCallKind,
path: ModPath,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index 51c565fe1..2dcc2c30f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -74,12 +74,20 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
}
None if file_id.is_include_macro(db.upcast()) => {
- candidate_files.push(format!("{name}.rs"));
- candidate_files.push(format!("{name}/mod.rs"));
+ candidate_files.push(format!("{}.rs", name.display(db.upcast())));
+ candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
}
None => {
- candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
- candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
+ candidate_files.push(format!(
+ "{}{}.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
+ candidate_files.push(format!(
+ "{}{}/mod.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
}
};
@@ -91,7 +99,7 @@ impl ModDir {
let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
(DirPath::empty(), false)
} else {
- (DirPath::new(format!("{name}/")), true)
+ (DirPath::new(format!("{}/", name.display(db.upcast()))), true)
};
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
return Ok((file_id, is_mod_rs, mod_dir));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 25478481d..5f6163175 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -16,11 +16,11 @@ use hir_expand::name::Name;
use crate::{
db::DefDatabase,
item_scope::BUILTIN_SCOPE,
- nameres::{BuiltinShadowMode, DefMap},
+ nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
+ AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -58,18 +58,22 @@ impl ResolvePathResult {
}
}
-impl DefMap {
- pub(super) fn resolve_name_in_extern_prelude(
- &self,
+impl PerNs {
+ pub(super) fn filter_macro(
+ mut self,
db: &dyn DefDatabase,
- name: &Name,
- ) -> Option<ModuleId> {
- match self.block {
- Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
- None => self.extern_prelude.get(name).copied(),
- }
+ expected: Option<MacroSubNs>,
+ ) -> Self {
+ self.macros = self.macros.filter(|&(id, _)| {
+ let this = MacroSubNs::from_id(db, id);
+ sub_namespace_match(Some(this), expected)
+ });
+
+ self
}
+}
+impl DefMap {
pub(crate) fn resolve_visibility(
&self,
db: &dyn DefDatabase,
@@ -83,7 +87,7 @@ impl DefMap {
let mut vis = match visibility {
RawVisibility::Module(path) => {
let (result, remaining) =
- self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
+ self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
if remaining.is_some() {
return None;
}
@@ -106,7 +110,7 @@ impl DefMap {
// ...unless we're resolving visibility for an associated item in an impl.
if self.block_id() != m.block && !within_impl {
cov_mark::hit!(adjust_vis_in_block_def_map);
- vis = Visibility::Module(self.module_id(self.root()));
+ vis = Visibility::Module(self.module_id(Self::ROOT));
tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
}
}
@@ -124,6 +128,9 @@ impl DefMap {
mut original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
+ // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
@@ -136,6 +143,7 @@ impl DefMap {
original_module,
path,
shadow,
+ expected_macro_subns,
);
// Merge `new` into `result`.
@@ -154,7 +162,7 @@ impl DefMap {
match &current_map.block {
Some(block) => {
original_module = block.parent.local_id;
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, current_map.krate);
current_map = &*arc;
}
None => return result,
@@ -169,11 +177,15 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let graph = db.crate_graph();
let _cx = stdx::panic_context::enter(format!(
- "DefMap {:?} crate_name={:?} block={:?} path={path}",
- self.krate, graph[self.krate].display_name, self.block
+ "DefMap {:?} crate_name={:?} block={:?} path={}",
+ self.krate,
+ graph[self.krate].display_name,
+ self.block,
+ path.display(db.upcast())
));
let mut segments = path.segments().iter().enumerate();
@@ -181,21 +193,21 @@ impl DefMap {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
- PerNs::types(self.crate_root(db).into(), Visibility::Public)
+ PerNs::types(self.crate_root().into(), Visibility::Public)
} else {
let def_map = db.crate_def_map(krate);
- let module = def_map.module_id(def_map.root);
+ let module = def_map.module_id(Self::ROOT);
cov_mark::hit!(macro_dollar_crate_other);
PerNs::types(module.into(), Visibility::Public)
}
}
- PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
+ PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
- if self.edition == Edition::Edition2015
+ if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{
let (_, segment) = match segments.next() {
@@ -220,7 +232,13 @@ impl DefMap {
if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
tracing::debug!("resolving {:?} in module", segment);
- self.resolve_name_in_module(db, original_module, segment, prefer_module)
+ self.resolve_name_in_module(
+ db,
+ original_module,
+ segment,
+ prefer_module,
+ expected_macro_subns,
+ )
}
PathKind::Super(lvl) => {
let mut module = original_module;
@@ -236,16 +254,20 @@ impl DefMap {
);
tracing::debug!(
"`super` path: {} -> {} in parent map",
- path,
- new_path
- );
- return block.parent.def_map(db).resolve_path_fp_with_macro(
- db,
- mode,
- block.parent.local_id,
- &new_path,
- shadow,
+ path.display(db.upcast()),
+ new_path.display(db.upcast())
);
+ return block
+ .parent
+ .def_map(db, self.krate)
+ .resolve_path_fp_with_macro(
+ db,
+ mode,
+ block.parent.local_id,
+ &new_path,
+ shadow,
+ expected_macro_subns,
+ );
}
None => {
tracing::debug!("super path in root module");
@@ -271,7 +293,7 @@ impl DefMap {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&def) = self.extern_prelude.get(segment) {
+ if let Some(&def) = self.data.extern_prelude.get(segment) {
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
PerNs::types(def.into(), Visibility::Public)
} else {
@@ -303,7 +325,12 @@ impl DefMap {
);
tracing::debug!("resolving {:?} in other crate", path);
let defp_map = module.def_map(db);
- let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
+ // Macro sub-namespaces only matter when resolving single-segment paths
+ // because `macro_use` and other preludes should be taken into account. At
+ // this point, we know we're resolving a multi-segment path so macro kind
+ // expectation is discarded.
+ let (def, s) =
+ defp_map.resolve_path(db, module.local_id, &path, shadow, None);
return ResolvePathResult::with(
def,
ReachedFixedPoint::Yes,
@@ -331,11 +358,11 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
- crate::adt::VariantData::Record(_) => {
+ crate::data::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public)
}
- crate::adt::VariantData::Tuple(_)
- | crate::adt::VariantData::Unit => {
+ crate::data::adt::VariantData::Tuple(_)
+ | crate::data::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public)
}
}
@@ -381,19 +408,24 @@ impl DefMap {
module: LocalModuleId,
name: &Name,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> PerNs {
// Resolve in:
// - legacy scope of macro
// - current module / scope
- // - extern prelude
+ // - extern prelude / macro_use prelude
// - std prelude
let from_legacy_macro = self[module]
.scope
.get_legacy_macro(name)
// FIXME: shadowing
.and_then(|it| it.last())
- .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
- let from_scope = self[module].scope.get(name);
+ .copied()
+ .filter(|&id| {
+ sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
+ })
+ .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+ let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
let from_builtin = match self.block {
Some(_) => {
// Only resolve to builtins in the root `DefMap`.
@@ -410,13 +442,27 @@ impl DefMap {
};
let extern_prelude = || {
- self.extern_prelude
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
.get(name)
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
};
+ let macro_use_prelude = || {
+ self.macro_use_prelude
+ .get(name)
+ .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+ };
let prelude = || self.resolve_in_prelude(db, name);
- from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
+ from_legacy_macro
+ .or(from_scope_or_builtin)
+ .or_else(extern_prelude)
+ .or_else(macro_use_prelude)
+ .or_else(prelude)
}
fn resolve_name_in_crate_root_or_extern_prelude(
@@ -426,13 +472,20 @@ impl DefMap {
) -> PerNs {
let from_crate_root = match self.block {
Some(_) => {
- let def_map = self.crate_root(db).def_map(db);
- def_map[def_map.root].scope.get(name)
+ let def_map = self.crate_root().def_map(db);
+ def_map[Self::ROOT].scope.get(name)
}
- None => self[self.root].scope.get(name),
+ None => self[Self::ROOT].scope.get(name),
};
let from_extern_prelude = || {
- self.resolve_name_in_extern_prelude(db, name)
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
+ .get(name)
+ .copied()
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
index caad4a1f3..751b7beaa 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
@@ -52,7 +52,7 @@ impl Attrs {
}
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
-// the same strucuture.
+// the same structure.
#[rustfmt::skip]
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
match tt {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 8a27c60df..dd7c3c363 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -4,10 +4,9 @@ mod macros;
mod mod_resolution;
mod primitives;
-use std::sync::Arc;
-
use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::{expect, Expect};
+use triomphe::Arc;
use crate::{db::DefDatabase, test_db::TestDB};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index 13e6825f8..4931c36bb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,8 +1,7 @@
-use std::sync::Arc;
-
use base_db::SourceDatabaseExt;
+use triomphe::Arc;
-use crate::{AdtId, ModuleDefId};
+use crate::{db::DefDatabase, AdtId, ModuleDefId};
use super::*;
@@ -15,7 +14,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
{
let events = db.log_executed(|| {
@@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
+ db.set_file_text(pos.file_id, Arc::from("m!(Y);"));
{
let events = db.log_executed(|| {
@@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
}
#[test]
-fn typing_inside_a_function_should_not_invalidate_expansions() {
+fn typing_inside_a_function_should_not_invalidate_item_expansions() {
let (mut db, pos) = TestDB::with_position(
r#"
//- /lib.rs
@@ -140,7 +139,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 3);
}
@@ -150,7 +149,7 @@ fn quux() { 92 }
m!(Y);
m!(Z);
"#;
- db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
{
let events = db.log_executed(|| {
@@ -161,7 +160,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 1);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 0);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index a4ccd14cb..f4cca8d68 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -260,6 +260,72 @@ mod priv_mod {
}
#[test]
+fn macro_use_filter() {
+ check(
+ r#"
+//- /main.rs crate:main deps:empty,multiple,all
+#[macro_use()]
+extern crate empty;
+
+foo_not_imported!();
+
+#[macro_use(bar1)]
+#[macro_use()]
+#[macro_use(bar2, bar3)]
+extern crate multiple;
+
+bar1!();
+bar2!();
+bar3!();
+bar_not_imported!();
+
+#[macro_use(baz1)]
+#[macro_use]
+#[macro_use(baz2)]
+extern crate all;
+
+baz1!();
+baz2!();
+baz3!();
+
+//- /empty.rs crate:empty
+#[macro_export]
+macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
+
+//- /multiple.rs crate:multiple
+#[macro_export]
+macro_rules! bar1 { () => { struct OkBar1; } }
+#[macro_export]
+macro_rules! bar2 { () => { struct OkBar2; } }
+#[macro_export]
+macro_rules! bar3 { () => { struct OkBar3; } }
+#[macro_export]
+macro_rules! bar_not_imported { () => { struct NotOkBar; } }
+
+//- /all.rs crate:all
+#[macro_export]
+macro_rules! baz1 { () => { struct OkBaz1; } }
+#[macro_export]
+macro_rules! baz2 { () => { struct OkBaz2; } }
+#[macro_export]
+macro_rules! baz3 { () => { struct OkBaz3; } }
+"#,
+ expect![[r#"
+ crate
+ OkBar1: t v
+ OkBar2: t v
+ OkBar3: t v
+ OkBaz1: t v
+ OkBaz2: t v
+ OkBaz3: t v
+ all: t
+ empty: t
+ multiple: t
+ "#]],
+ );
+}
+
+#[test]
fn prelude_is_macro_use() {
cov_mark::check!(prelude_is_macro_use);
check(
@@ -665,6 +731,29 @@ pub struct bar;
}
#[test]
+fn macro_dollar_crate_is_correct_in_derive_meta() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+//- /main.rs crate:main deps:lib
+lib::foo!();
+
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! foo {
+ () => {
+ #[derive($crate::Clone)]
+ struct S;
+ }
+}
+
+pub use core::clone::Clone;
+"#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
fn expand_derive() {
let map = compute_crate_def_map(
r#"
@@ -683,7 +772,7 @@ pub macro Copy {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 2);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
}
#[test]
@@ -726,7 +815,7 @@ pub macro derive($item:item) {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 1);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
@@ -991,7 +1080,7 @@ macro_rules! mbe {
#[test]
fn collects_derive_helpers() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
#![crate_type="proc-macro"]
struct TokenStream;
@@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
}
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
- assert_eq!(def_map.exported_derives.len(), 1);
- match def_map.exported_derives.values().next() {
+ assert_eq!(def_map.data.exported_derives.len(), 1);
+ match def_map.data.exported_derives.values().next() {
Some(helpers) => match &**helpers {
- [attr] => assert_eq!(attr.to_string(), "helper_attr"),
+ [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
_ => unreachable!(),
},
_ => unreachable!(),
@@ -1169,7 +1260,7 @@ struct A;
#[test]
fn macro_use_imports_all_macro_types() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
//- /main.rs crate:main deps:lib
#[macro_use]
@@ -1192,18 +1283,153 @@ struct TokenStream;
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
+
+ let root_module = &def_map[DefMap::ROOT].scope;
+ assert!(
+ root_module.legacy_macros().count() == 0,
+ "`#[macro_use]` shouldn't bring macros into textual macro scope",
+ );
- let root = &def_map[def_map.root()].scope;
- let actual = root
- .legacy_macros()
- .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
- .map(|(name, _)| format!("{name}\n"))
- .collect::<String>();
+ let actual = def_map
+ .macro_use_prelude
+ .iter()
+ .map(|(name, _)| name.display(&db).to_string())
+ .sorted()
+ .join("\n");
expect![[r#"
legacy
macro20
- proc_attr
- "#]]
+ proc_attr"#]]
.assert_eq(&actual);
}
+
+#[test]
+fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
+ check(
+ r#"
+//- /lib.rs edition:2021 crate:lib deps:dep,core
+#[macro_use]
+extern crate dep;
+
+macro foo() { struct Ok; }
+macro bar() { fn ok() {} }
+
+foo!();
+bar!();
+
+//- /dep.rs crate:dep
+#[macro_export]
+macro_rules! foo {
+ () => { struct NotOk; }
+}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ #[macro_export]
+ macro_rules! bar {
+ () => { fn not_ok() {} }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ dep: t
+ foo: m
+ ok: v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_use_prelude_is_eagerly_expanded() {
+ // See FIXME in `ModCollector::collect_macro_call()`.
+ check(
+ r#"
+//- /main.rs crate:main deps:lib
+#[macro_use]
+extern crate lib;
+mk_foo!();
+mod a {
+ foo!();
+}
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! mk_foo {
+ () => {
+ macro_rules! foo {
+ () => { struct Ok; }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ a: t
+ lib: t
+
+ crate::a
+ Ok: t v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_sub_namespace() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+macro_rules! Clone { () => {} }
+macro_rules! derive { () => {} }
+
+#[derive(Clone)]
+struct S;
+ "#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
+fn macro_sub_namespace2() {
+ check(
+ r#"
+//- /main.rs edition:2021 crate:main deps:proc,core
+use proc::{foo, bar};
+
+foo!();
+bar!();
+
+//- /proc.rs crate:proc
+#![crate_type="proc-macro"]
+#[proc_macro_derive(foo)]
+pub fn foo() {}
+#[proc_macro_attribute]
+pub fn bar() {}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ pub macro foo() {
+ struct Ok;
+ }
+ pub macro bar() {
+ fn ok() {}
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ foo: m
+ ok: v
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index a01931288..81bc0ff91 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -887,7 +887,7 @@ mod module;
//- /module.rs
#![cfg(NEVER)]
-struct AlsoShoulntAppear;
+struct AlsoShouldNotAppear;
"#,
expect![[r#"
crate
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index f3197d180..ff4ae6954 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -7,15 +7,14 @@ use std::{
};
use crate::{
- body::LowerCtx,
- type_ref::{ConstRefOrPath, LifetimeRef},
+ lang_item::LangItemTarget,
+ lower::LowerCtx,
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
};
use hir_expand::name::Name;
use intern::Interned;
use syntax::ast;
-use crate::type_ref::{TypeBound, TypeRef};
-
pub use hir_expand::mod_path::{path, ModPath, PathKind};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,13 +35,19 @@ impl Display for ImportAlias {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Path {
- /// Type based path like `<T>::foo`.
- /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
- type_anchor: Option<Interned<TypeRef>>,
- mod_path: Interned<ModPath>,
- /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
- generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+pub enum Path {
+ /// A normal path
+ Normal {
+ /// Type based path like `<T>::foo`.
+ /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
+ type_anchor: Option<Interned<TypeRef>>,
+ mod_path: Interned<ModPath>,
+ /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
+ generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+ },
+ /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
+ /// links via a normal path since they might be private and not accessible in the usage place.
+ LangItem(LangItemTarget),
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -85,7 +90,7 @@ pub struct AssociatedTypeBinding {
pub enum GenericArg {
Type(TypeRef),
Lifetime(LifetimeRef),
- Const(ConstRefOrPath),
+ Const(ConstRef),
}
impl Path {
@@ -102,51 +107,77 @@ impl Path {
) -> Path {
let generic_args = generic_args.into();
assert_eq!(path.len(), generic_args.len());
- Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
+ Path::Normal {
+ type_anchor: None,
+ mod_path: Interned::new(path),
+ generic_args: Some(generic_args),
+ }
+ }
+
+ /// Converts a known mod path to `Path`.
+ pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
+ Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
}
pub fn kind(&self) -> &PathKind {
- &self.mod_path.kind
+ match self {
+ Path::Normal { mod_path, .. } => &mod_path.kind,
+ Path::LangItem(_) => &PathKind::Abs,
+ }
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
- self.type_anchor.as_deref()
+ match self {
+ Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
+ Path::LangItem(_) => None,
+ }
}
pub fn segments(&self) -> PathSegments<'_> {
- let s = PathSegments {
- segments: self.mod_path.segments(),
- generic_args: self.generic_args.as_deref(),
+ let Path::Normal { mod_path, generic_args, .. } = self else {
+ return PathSegments {
+ segments: &[],
+ generic_args: None,
+ };
};
+ let s =
+ PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len());
}
s
}
- pub fn mod_path(&self) -> &ModPath {
- &self.mod_path
+ pub fn mod_path(&self) -> Option<&ModPath> {
+ match self {
+ Path::Normal { mod_path, .. } => Some(&mod_path),
+ Path::LangItem(_) => None,
+ }
}
pub fn qualifier(&self) -> Option<Path> {
- if self.mod_path.is_ident() {
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return None;
+ };
+ if mod_path.is_ident() {
return None;
}
- let res = Path {
- type_anchor: self.type_anchor.clone(),
+ let res = Path::Normal {
+ type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments(
- self.mod_path.kind,
- self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
+ mod_path.kind,
+ mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
)),
- generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
+ generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
};
Some(res)
}
pub fn is_self_type(&self) -> bool {
- self.type_anchor.is_none()
- && self.generic_args.as_deref().is_none()
- && self.mod_path.is_Self()
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return false;
+ };
+ type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
}
}
@@ -222,7 +253,7 @@ impl GenericArgs {
impl From<Name> for Path {
fn from(name: Name) -> Path {
- Path {
+ Path::Normal {
type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index b7542bd77..1cb17ff0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,17 +2,15 @@
use std::iter;
-use crate::type_ref::ConstRefOrPath;
+use crate::{lower::LowerCtx, type_ref::ConstRef};
use either::Either;
use hir_expand::name::{name, AsName};
use intern::Interned;
use syntax::ast::{self, AstNode, HasTypeBounds};
-use super::AssociatedTypeBinding;
use crate::{
- body::LowerCtx,
- path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
+ path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
@@ -75,8 +73,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
- let Path { mod_path, generic_args: path_generic_args, .. } =
- Path::from_src(trait_ref.path()?, ctx)?;
+ let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
+ Path::from_src(trait_ref.path()?, ctx)? else
+ {
+ return None;
+ };
let num_segments = mod_path.segments().len();
kind = mod_path.kind;
@@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
- return Some(Path {
+ return Some(Path::Normal {
type_anchor,
mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
@@ -188,6 +189,10 @@ pub(super) fn lower_generic_args(
args.push(GenericArg::Type(type_ref));
}
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
+ if assoc_type_arg.param_list().is_some() {
+ // We currently ignore associated return type bounds.
+ continue;
+ }
if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name();
let args = assoc_type_arg
@@ -212,7 +217,7 @@ pub(super) fn lower_generic_args(
}
}
ast::GenericArg::ConstArg(arg) => {
- let arg = ConstRefOrPath::from_expr_opt(arg.expr());
+ let arg = ConstRef::from_const_arg(lower_ctx, Some(arg));
args.push(GenericArg::Const(arg))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 2d45c8c8d..0aead6f37 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,7 +2,7 @@
use std::fmt::{self, Write};
-use hir_expand::mod_path::PathKind;
+use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
use intern::Interned;
use itertools::Itertools;
@@ -11,11 +11,14 @@ use crate::{
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+ if let Path::LangItem(x) = path {
+ return write!(buf, "$lang_item::{x:?}");
+ }
match path.type_anchor() {
Some(anchor) => {
write!(buf, "<")?;
- print_type_ref(anchor, buf)?;
+ print_type_ref(db, anchor, buf)?;
write!(buf, ">::")?;
}
None => match path.kind() {
@@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name)?;
+ write!(buf, "{}", segment.name.display(db))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
- print_generic_args(generics, buf)?;
+ print_generic_args(db, generics, buf)?;
write!(buf, ">")?;
}
@@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
Ok(())
}
-pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_args(
+ db: &dyn ExpandDatabase,
+ generics: &GenericArgs,
+ buf: &mut dyn Write,
+) -> fmt::Result {
let mut first = true;
let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?;
- print_generic_arg(self_ty, buf)?;
+ print_generic_arg(db, self_ty, buf)?;
first = false;
args
} else {
@@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
write!(buf, ", ")?;
}
first = false;
- print_generic_arg(arg, buf)?;
+ print_generic_arg(db, arg, buf)?;
}
for binding in generics.bindings.iter() {
if !first {
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name)?;
+ write!(buf, "{}", binding.name.display(db))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
- print_type_bounds(&binding.bounds, buf)?;
+ print_type_bounds(db, &binding.bounds, buf)?;
}
if let Some(ty) = &binding.type_ref {
write!(buf, " = ")?;
- print_type_ref(ty, buf)?;
+ print_type_ref(db, ty, buf)?;
}
}
Ok(())
}
-pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_arg(
+ db: &dyn ExpandDatabase,
+ arg: &GenericArg,
+ buf: &mut dyn Write,
+) -> fmt::Result {
match arg {
- GenericArg::Type(ty) => print_type_ref(ty, buf),
- GenericArg::Const(c) => write!(buf, "{c}"),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
+ GenericArg::Type(ty) => print_type_ref(db, ty, buf),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
}
}
-pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_type_ref(
+ db: &dyn ExpandDatabase,
+ type_ref: &TypeRef,
+ buf: &mut dyn Write,
+) -> fmt::Result {
// FIXME: deduplicate with `HirDisplay` impl
match type_ref {
TypeRef::Never => write!(buf, "!")?,
@@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(field, buf)?;
+ print_type_ref(db, field, buf)?;
}
write!(buf, ")")?;
}
- TypeRef::Path(path) => print_path(path, buf)?,
+ TypeRef::Path(path) => print_path(db, path, buf)?,
TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "*const",
Mutability::Mut => "*mut",
};
write!(buf, "{mtbl} ")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Reference(pointee, lt, mtbl) => {
let mtbl = match mtbl {
@@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name)?;
+ write!(buf, "{} ", lt.name.display(db))?;
}
write!(buf, "{mtbl}")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
- write!(buf, "; {len}]")?;
+ print_type_ref(db, elem, buf)?;
+ write!(buf, "; {}]", len.display(db))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
+ print_type_ref(db, elem, buf)?;
write!(buf, "]")?;
}
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
@@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(typeref, buf)?;
+ print_type_ref(db, typeref, buf)?;
}
if *varargs {
if !args.is_empty() {
@@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
write!(buf, "...")?;
}
write!(buf, ") -> ")?;
- print_type_ref(return_type, buf)?;
+ print_type_ref(db, return_type, buf)?;
}
TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?;
@@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
TypeRef::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
}
@@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
}
pub(crate) fn print_type_bounds(
+ db: &dyn ExpandDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
- print_path(path, buf)?;
+ print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
- write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
- print_path(path, buf)?;
+ write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+ print_path(db, path, buf)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 61e64fc10..0d6f55411 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
//! Name resolution façade.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use hir_expand::name::{name, Name};
@@ -7,23 +7,25 @@ use indexmap::IndexMap;
use intern::Interned;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
+use triomphe::Arc;
use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
db::DefDatabase,
- expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData},
+ hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
- nameres::DefMap,
- path::{ModPath, PathKind},
+ lang_item::LangItemTarget,
+ nameres::{DefMap, MacroSubNs},
+ path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
- FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
- LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId,
- StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
- VariantId,
+ AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
+ EnumVariantId, ExternBlockId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+ ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
+ ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+ TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId,
};
#[derive(Debug, Clone)]
@@ -78,7 +80,7 @@ enum Scope {
ExprScope(ExprScope),
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeNs {
SelfType(ImplId),
GenericParam(TypeParamId),
@@ -153,7 +155,8 @@ impl Resolver {
path: &ModPath,
) -> Option<PerNs> {
let (item_map, module) = self.item_scope();
- let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
+ let (module_res, idx) =
+ item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
match module_res.take_types()? {
ModuleDefId::TraitId(it) => {
let idx = idx?;
@@ -176,8 +179,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<(TypeNs, Option<usize>)> {
+ let path = match path {
+ Path::Normal { mod_path, .. } => mod_path,
+ Path::LangItem(l) => {
+ return Some((
+ match *l {
+ LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
+ LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
+ LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::Trait(x) => TypeNs::TraitId(x),
+ LangItemTarget::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ },
+ None,
+ ))
+ }
+ };
let first_name = path.segments().first()?;
let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod {
@@ -217,7 +239,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
@@ -245,8 +267,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ResolveValueResult> {
+ let path = match path {
+ Path::Normal { mod_path, .. } => mod_path,
+ Path::LangItem(l) => {
+ return Some(ResolveValueResult::ValueNs(match *l {
+ LangItemTarget::Function(x) => ValueNs::FunctionId(x),
+ LangItemTarget::Static(x) => ValueNs::StaticId(x),
+ LangItemTarget::Struct(x) => ValueNs::StructId(x),
+ LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
+ LangItemTarget::Union(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::TypeAlias(_)
+ | LangItemTarget::Trait(_)
+ | LangItemTarget::EnumId(_) => return None,
+ }))
+ }
+ };
let n_segments = path.segments().len();
let tmp = name![self];
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
@@ -340,7 +378,7 @@ impl Resolver {
pub fn resolve_path_in_value_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it),
@@ -348,9 +386,17 @@ impl Resolver {
}
}
- pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
+ pub fn resolve_path_as_macro(
+ &self,
+ db: &dyn DefDatabase,
+ path: &ModPath,
+ expected_macro_kind: Option<MacroSubNs>,
+ ) -> Option<MacroId> {
let (item_map, module) = self.item_scope();
- item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
+ item_map
+ .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
+ .0
+ .take_macros()
}
/// Returns a set of names available in the current scope.
@@ -415,7 +461,10 @@ impl Resolver {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
})
});
- def_map.extern_prelude().for_each(|(name, &def)| {
+ def_map.macro_use_prelude().for_each(|(name, def)| {
+ res.add(name, ScopeDef::ModuleDef(def.into()));
+ });
+ def_map.extern_prelude().for_each(|(name, def)| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
});
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@@ -441,7 +490,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(trait_)) =
- self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
+ self.resolve_path_in_type_ns_fully(db, &target_trait.path)
{
traits.insert(trait_);
}
@@ -536,15 +585,13 @@ impl Resolver {
scope_id,
}));
if let Some(block) = expr_scopes.block(scope_id) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- resolver
- .scopes
- .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ resolver
+ .scopes
+ .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
}
@@ -592,7 +639,8 @@ impl Resolver {
shadow: BuiltinShadowMode,
) -> PerNs {
let (item_map, module) = self.item_scope();
- let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
+ // This method resolves `path` just like import paths, so no expected macro subns is given.
+ let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
if segment_index.is_some() {
return PerNs::none();
}
@@ -705,13 +753,11 @@ fn resolver_for_scope_(
for scope in scope_chain.into_iter().rev() {
if let Some(block) = scopes.block(scope) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- r = r.push_block_scope(def_map, root);
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ r = r.push_block_scope(def_map, DefMap::ROOT);
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
@@ -900,6 +946,15 @@ impl HasResolver for ModuleId {
}
}
+impl HasResolver for CrateRootModuleId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ Resolver {
+ scopes: vec![],
+ module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
+ }
+ }
+}
+
impl HasResolver for TraitId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
@@ -963,6 +1018,24 @@ impl HasResolver for ExternBlockId {
}
}
+impl HasResolver for TypeOwnerId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ match self {
+ TypeOwnerId::FunctionId(x) => x.resolver(db),
+ TypeOwnerId::StaticId(x) => x.resolver(db),
+ TypeOwnerId::ConstId(x) => x.resolver(db),
+ TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db),
+ TypeOwnerId::AdtId(x) => x.resolver(db),
+ TypeOwnerId::TraitId(x) => x.resolver(db),
+ TypeOwnerId::TraitAliasId(x) => x.resolver(db),
+ TypeOwnerId::TypeAliasId(x) => x.resolver(db),
+ TypeOwnerId::ImplId(x) => x.resolver(db),
+ TypeOwnerId::EnumVariantId(x) => x.resolver(db),
+ TypeOwnerId::ModuleId(x) => x.resolver(db),
+ }
+ }
+}
+
impl HasResolver for DefWithBodyId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
@@ -970,6 +1043,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
}
}
}
@@ -1000,6 +1074,12 @@ impl HasResolver for GenericDefId {
}
}
+impl HasResolver for EnumVariantId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ self.parent.resolver(db)
+ }
+}
+
impl HasResolver for VariantId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index f69356cac..6047f770d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -33,7 +33,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -46,7 +46,7 @@ impl HasSource for Macro2Loc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index ee143b19a..a6befc8a8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -1,17 +1,16 @@
//! Database used for testing `hir_def`.
-use std::{
- fmt, panic,
- sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
use base_db::{
- salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
- SourceDatabase, Upcast,
+ salsa::{self, Durability},
+ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
+ Upcast,
};
use hir_expand::{db::ExpandDatabase, InFile};
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
use syntax::{algo, ast, AstNode};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -35,7 +34,7 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
- this.set_enable_proc_attr_macros(true);
+ this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
this
}
}
@@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -111,7 +110,7 @@ impl TestDB {
}
_ => {
// FIXME: handle `mod` inside block expression
- return def_map.module_id(def_map.root());
+ return def_map.module_id(DefMap::ROOT);
}
}
}
@@ -120,7 +119,7 @@ impl TestDB {
/// Finds the smallest/innermost module in `def_map` containing `position`.
fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
let mut size = None;
- let mut res = def_map.root();
+ let mut res = DefMap::ROOT;
for (module, data) in def_map.modules() {
let src = data.definition_source(self);
if src.file_id != position.file_id.into() {
@@ -209,13 +208,11 @@ impl TestDB {
});
for scope in scope_iter {
- let containing_blocks =
+ let mut containing_blocks =
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
- for block in containing_blocks {
- if let Some(def_map) = self.block_def_map(block) {
- return Some(def_map);
- }
+ if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
+ return Some(block);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index ab76ed43d..30f48de61 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
@@ -1,10 +1,11 @@
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
-use std::{iter, sync::Arc};
+use std::iter;
use hir_expand::{hygiene::Hygiene, InFile};
use la_arena::ArenaMap;
use syntax::ast;
+use triomphe::Arc;
use crate::{
db::DefDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 5c684be03..40d8659f2 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -22,6 +22,7 @@ hashbrown = { version = "0.12.1", features = [
"inline-more",
], default-features = false }
smallvec.workspace = true
+triomphe.workspace = true
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
index 2b27db0e9..c2b0d5985 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
@@ -20,7 +20,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
/// `AstId` points to an AST node in a specific file.
pub struct FileAstId<N: AstNode> {
raw: ErasedFileAstId,
- _ty: PhantomData<fn() -> N>,
+ covariant: PhantomData<fn() -> N>,
}
impl<N: AstNode> Clone for FileAstId<N> {
@@ -54,7 +54,7 @@ impl<N: AstNode> FileAstId<N> {
where
N: Into<M>,
{
- FileAstId { raw: self.raw, _ty: PhantomData }
+ FileAstId { raw: self.raw, covariant: PhantomData }
}
}
@@ -98,6 +98,7 @@ impl AstIdMap {
|| ast::Variant::can_cast(kind)
|| ast::RecordField::can_cast(kind)
|| ast::TupleField::can_cast(kind)
+ || ast::ConstArg::can_cast(kind)
{
res.alloc(&it);
true
@@ -115,12 +116,17 @@ impl AstIdMap {
}
}
}
+ res.arena.shrink_to_fit();
res
}
pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
let raw = self.erased_ast_id(item.syntax());
- FileAstId { raw, _ty: PhantomData }
+ FileAstId { raw, covariant: PhantomData }
+ }
+
+ pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
+ AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
}
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
@@ -136,10 +142,6 @@ impl AstIdMap {
}
}
- pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
- AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
- }
-
fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
self.arena.alloc(SyntaxNodePtr::new(item))
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 8d1e88725..4c918e55b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{fmt, ops, sync::Arc};
+use std::{fmt, ops};
use base_db::CrateId;
use cfg::CfgExpr;
@@ -8,12 +8,12 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec};
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
+use triomphe::Arc;
use crate::{
db::ExpandDatabase,
hygiene::Hygiene,
- mod_path::{ModPath, PathKind},
- name::AsName,
+ mod_path::ModPath,
tt::{self, Subtree},
InFile,
};
@@ -21,6 +21,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
+ // FIXME: Make this a ThinArc
entries: Option<Arc<[Attr]>>,
}
@@ -50,7 +51,9 @@ impl RawAttrs {
path: Interned::new(ModPath::from(crate::name!(doc))),
}),
})
- .collect::<Arc<_>>();
+ .collect::<Vec<_>>();
+ // FIXME: use `Arc::from_iter` when it becomes available
+ let entries: Arc<[Attr]> = Arc::from(entries);
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
}
@@ -68,7 +71,7 @@ impl RawAttrs {
(Some(a), Some(b)) => {
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
Self {
- entries: Some(
+ entries: Some(Arc::from(
a.iter()
.cloned()
.chain(b.iter().map(|it| {
@@ -78,8 +81,9 @@ impl RawAttrs {
<< AttrId::AST_INDEX_BITS;
it
}))
- .collect(),
- ),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ )),
}
}
}
@@ -96,48 +100,51 @@ impl RawAttrs {
}
let crate_graph = db.crate_graph();
- let new_attrs = self
- .iter()
- .flat_map(|attr| -> SmallVec<[_; 1]> {
- let is_cfg_attr =
- attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
- if !is_cfg_attr {
- return smallvec![attr.clone()];
- }
-
- let subtree = match attr.token_tree_value() {
- Some(it) => it,
- _ => return smallvec![attr.clone()],
- };
-
- let (cfg, parts) = match parse_cfg_attr_input(subtree) {
- Some(it) => it,
- None => return smallvec![attr.clone()],
- };
- let index = attr.id;
- let attrs =
- parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
- let tree = Subtree {
- delimiter: tt::Delimiter::unspecified(),
- token_trees: attr.to_vec(),
- };
- // FIXME hygiene
- let hygiene = Hygiene::new_unhygienic();
- Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
- });
-
- let cfg_options = &crate_graph[krate].cfg_options;
- let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
- let cfg = CfgExpr::parse(&cfg);
- if cfg_options.check(&cfg) == Some(false) {
- smallvec![]
- } else {
- cov_mark::hit!(cfg_attr_active);
-
- attrs.collect()
- }
- })
- .collect();
+ let new_attrs = Arc::from(
+ self.iter()
+ .flat_map(|attr| -> SmallVec<[_; 1]> {
+ let is_cfg_attr =
+ attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
+ if !is_cfg_attr {
+ return smallvec![attr.clone()];
+ }
+
+ let subtree = match attr.token_tree_value() {
+ Some(it) => it,
+ _ => return smallvec![attr.clone()],
+ };
+
+ let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+ Some(it) => it,
+ None => return smallvec![attr.clone()],
+ };
+ let index = attr.id;
+ let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
+ |(idx, attr)| {
+ let tree = Subtree {
+ delimiter: tt::Delimiter::unspecified(),
+ token_trees: attr.to_vec(),
+ };
+ // FIXME hygiene
+ let hygiene = Hygiene::new_unhygienic();
+ Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
+ },
+ );
+
+ let cfg_options = &crate_graph[krate].cfg_options;
+ let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
+ let cfg = CfgExpr::parse(&cfg);
+ if cfg_options.check(&cfg) == Some(false) {
+ smallvec![]
+ } else {
+ cov_mark::hit!(cfg_attr_active);
+
+ attrs.collect()
+ }
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
RawAttrs { entries: Some(new_attrs) }
}
@@ -185,14 +192,14 @@ pub enum AttrInput {
/// `#[attr = "string"]`
Literal(SmolStr),
/// `#[attr(subtree)]`
- TokenTree(tt::Subtree, mbe::TokenMap),
+ TokenTree(Box<(tt::Subtree, mbe::TokenMap)>),
}
impl fmt::Display for AttrInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
- AttrInput::TokenTree(subtree, _) => subtree.fmt(f),
+ AttrInput::TokenTree(tt) => tt.0.fmt(f),
}
}
}
@@ -213,7 +220,7 @@ impl Attr {
Some(Interned::new(AttrInput::Literal(value)))
} else if let Some(tt) = ast.token_tree() {
let (tree, map) = syntax_node_to_token_tree(tt.syntax());
- Some(Interned::new(AttrInput::TokenTree(tree, map)))
+ Some(Interned::new(AttrInput::TokenTree(Box::new((tree, map)))))
} else {
None
};
@@ -249,7 +256,7 @@ impl Attr {
/// #[path(ident)]
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
match self.input.as_deref()? {
- AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
+ AttrInput::TokenTree(tt) => match &*tt.0.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
_ => None,
},
@@ -260,13 +267,17 @@ impl Attr {
/// #[path TokenTree]
pub fn token_tree_value(&self) -> Option<&Subtree> {
match self.input.as_deref()? {
- AttrInput::TokenTree(subtree, _) => Some(subtree),
+ AttrInput::TokenTree(tt) => Some(&tt.0),
_ => None,
}
}
/// Parses this attribute as a token tree consisting of comma separated paths.
- pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
+ pub fn parse_path_comma_token_tree<'a>(
+ &'a self,
+ db: &'a dyn ExpandDatabase,
+ hygiene: &'a Hygiene,
+ ) -> Option<impl Iterator<Item = ModPath> + 'a> {
let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis {
@@ -275,19 +286,37 @@ impl Attr {
let paths = args
.token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
- .filter_map(|tts| {
+ .filter_map(move |tts| {
if tts.is_empty() {
return None;
}
- let segments = tts.iter().filter_map(|tt| match tt {
- tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
- _ => None,
- });
- Some(ModPath::from_segments(PathKind::Plain, segments))
+ // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
+ let subtree = tt::Subtree {
+ delimiter: tt::Delimiter::unspecified(),
+ token_trees: tts.into_iter().cloned().collect(),
+ };
+ let (parse, _) =
+ mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem);
+ let meta = ast::Meta::cast(parse.syntax_node())?;
+ // Only simple paths are allowed.
+ if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some()
+ {
+ return None;
+ }
+ let path = meta.path()?;
+ ModPath::from_src(db, path, hygiene)
});
Some(paths)
}
+
+ pub fn cfg(&self) -> Option<CfgExpr> {
+ if *self.path.as_ident()? == crate::name![cfg] {
+ self.token_tree_value().map(CfgExpr::parse)
+ } else {
+ None
+ }
+ }
}
pub fn collect_attrs(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
index 277ecd939..80695bc06 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
@@ -96,7 +96,7 @@ fn derive_attr_expand(
) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro_call(id);
let derives = match &loc.kind {
- MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
+ MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => &attr_args.0,
_ => return ExpandResult::ok(tt::Subtree::empty()),
};
pseudo_derive_attr_expansion(tt, derives)
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 5c1a75132..3d1e272b9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,12 +1,19 @@
//! Builtin derives.
+use ::tt::Ident;
use base_db::{CrateOrigin, LangCrateOrigin};
+use itertools::izip;
+use mbe::TokenMap;
+use rustc_hash::FxHashSet;
+use stdx::never;
use tracing::debug;
-use crate::tt::{self, TokenId};
-use syntax::{
- ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
- match_ast,
+use crate::{
+ name::{AsName, Name},
+ tt::{self, TokenId},
+};
+use syntax::ast::{
+ self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
};
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
@@ -58,46 +65,201 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
BuiltinDeriveExpander::find_by_name(ident)
}
+enum VariantShape {
+ Struct(Vec<tt::Ident>),
+ Tuple(usize),
+ Unit,
+}
+
+fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
+ (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
+}
+
+impl VariantShape {
+ fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
+ self.as_pattern_map(path, |x| quote!(#x))
+ }
+
+ fn field_names(&self) -> Vec<tt::Ident> {
+ match self {
+ VariantShape::Struct(s) => s.clone(),
+ VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
+ VariantShape::Unit => vec![],
+ }
+ }
+
+ fn as_pattern_map(
+ &self,
+ path: tt::Subtree,
+ field_map: impl Fn(&tt::Ident) -> tt::Subtree,
+ ) -> tt::Subtree {
+ match self {
+ VariantShape::Struct(fields) => {
+ let fields = fields.iter().map(|x| {
+ let mapped = field_map(x);
+ quote! { #x : #mapped , }
+ });
+ quote! {
+ #path { ##fields }
+ }
+ }
+ &VariantShape::Tuple(n) => {
+ let fields = tuple_field_iterator(n).map(|x| {
+ let mapped = field_map(&x);
+ quote! {
+ #mapped ,
+ }
+ });
+ quote! {
+ #path ( ##fields )
+ }
+ }
+ VariantShape::Unit => path,
+ }
+ }
+
+ fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
+ let r = match value {
+ None => VariantShape::Unit,
+ Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
+ x.fields()
+ .map(|x| x.name())
+ .map(|x| name_to_token(token_map, x))
+ .collect::<Result<_, _>>()?,
+ ),
+ Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
+ };
+ Ok(r)
+ }
+}
+
+enum AdtShape {
+ Struct(VariantShape),
+ Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> },
+ Union,
+}
+
+impl AdtShape {
+ fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
+ self.as_pattern_map(name, |x| quote!(#x))
+ }
+
+ fn field_names(&self) -> Vec<Vec<tt::Ident>> {
+ match self {
+ AdtShape::Struct(s) => {
+ vec![s.field_names()]
+ }
+ AdtShape::Enum { variants, .. } => {
+ variants.iter().map(|(_, fields)| fields.field_names()).collect()
+ }
+ AdtShape::Union => {
+ never!("using fields of union in derive is always wrong");
+ vec![]
+ }
+ }
+ }
+
+ fn as_pattern_map(
+ &self,
+ name: &tt::Ident,
+ field_map: impl Fn(&tt::Ident) -> tt::Subtree,
+ ) -> Vec<tt::Subtree> {
+ match self {
+ AdtShape::Struct(s) => {
+ vec![s.as_pattern_map(quote! { #name }, field_map)]
+ }
+ AdtShape::Enum { variants, .. } => variants
+ .iter()
+ .map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
+ .collect(),
+ AdtShape::Union => {
+ never!("pattern matching on union is always wrong");
+ vec![quote! { un }]
+ }
+ }
+ }
+}
+
struct BasicAdtInfo {
name: tt::Ident,
- /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
- param_types: Vec<Option<tt::Subtree>>,
+ shape: AdtShape,
+ /// first field is the name, and
+ /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
+ /// third fields is where bounds, if any
+ param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
+ associated_types: Vec<tt::Subtree>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
debug!("derive node didn't parse");
- ExpandError::Other("invalid item definition".into())
+ ExpandError::other("invalid item definition")
})?;
let item = macro_items.items().next().ok_or_else(|| {
debug!("no module item parsed");
- ExpandError::Other("no item found".into())
+ ExpandError::other("no item found")
})?;
- let node = item.syntax();
- let (name, params) = match_ast! {
- match node {
- ast::Struct(it) => (it.name(), it.generic_param_list()),
- ast::Enum(it) => (it.name(), it.generic_param_list()),
- ast::Union(it) => (it.name(), it.generic_param_list()),
- _ => {
- debug!("unexpected node is {:?}", node);
- return Err(ExpandError::Other("expected struct, enum or union".into()))
- },
+ let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| {
+ debug!("expected adt, found: {:?}", item);
+ ExpandError::other("expected struct, enum or union")
+ })?;
+ let (name, generic_param_list, shape) = match &adt {
+ ast::Adt::Struct(it) => (
+ it.name(),
+ it.generic_param_list(),
+ AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?),
+ ),
+ ast::Adt::Enum(it) => {
+ let default_variant = it
+ .variant_list()
+ .into_iter()
+ .flat_map(|x| x.variants())
+ .position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
+ (
+ it.name(),
+ it.generic_param_list(),
+ AdtShape::Enum {
+ default_variant,
+ variants: it
+ .variant_list()
+ .into_iter()
+ .flat_map(|x| x.variants())
+ .map(|x| {
+ Ok((
+ name_to_token(&token_map, x.name())?,
+ VariantShape::from(x.field_list(), &token_map)?,
+ ))
+ })
+ .collect::<Result<_, ExpandError>>()?,
+ },
+ )
}
+ ast::Adt::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
};
- let name = name.ok_or_else(|| {
- debug!("parsed item has no name");
- ExpandError::Other("missing name".into())
- })?;
- let name_token_id =
- token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
- let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
- let param_types = params
+
+ let mut param_type_set: FxHashSet<Name> = FxHashSet::default();
+ let param_types = generic_param_list
.into_iter()
.flat_map(|param_list| param_list.type_or_const_params())
.map(|param| {
- if let ast::TypeOrConstParam::Const(param) = param {
+ let name = {
+ let this = param.name();
+ match this {
+ Some(x) => {
+ param_type_set.insert(x.as_name());
+ mbe::syntax_node_to_token_tree(x.syntax()).0
+ }
+ None => tt::Subtree::empty(),
+ }
+ };
+ let bounds = match &param {
+ ast::TypeOrConstParam::Type(x) => {
+ x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
+ }
+ ast::TypeOrConstParam::Const(_) => None,
+ };
+ let ty = if let ast::TypeOrConstParam::Const(param) = param {
let ty = param
.ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
@@ -105,27 +267,103 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
Some(ty)
} else {
None
- }
+ };
+ (name, ty, bounds)
+ })
+ .collect();
+
+ // For a generic parameter `T`, when shorthand associated type `T::Assoc` appears in field
+ // types (of any variant for enums), we generate trait bound for it. It sounds reasonable to
+ // also generate trait bound for qualified associated type `<T as Trait>::Assoc`, but rustc
+ // does not do that for some unknown reason.
+ //
+ // See the analogous function in rustc [find_type_parameters()] and rust-lang/rust#50730.
+ // [find_type_parameters()]: https://github.com/rust-lang/rust/blob/1.70.0/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs#L378
+
+ // It's cumbersome to deal with the distinct structures of ADTs, so let's just get untyped
+ // `SyntaxNode` that contains fields and look for descendant `ast::PathType`s. Of note is that
+ // we should not inspect `ast::PathType`s in parameter bounds and where clauses.
+ let field_list = match adt {
+ ast::Adt::Enum(it) => it.variant_list().map(|list| list.syntax().clone()),
+ ast::Adt::Struct(it) => it.field_list().map(|list| list.syntax().clone()),
+ ast::Adt::Union(it) => it.record_field_list().map(|list| list.syntax().clone()),
+ };
+ let associated_types = field_list
+ .into_iter()
+ .flat_map(|it| it.descendants())
+ .filter_map(ast::PathType::cast)
+ .filter_map(|p| {
+ let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
+ param_type_set.contains(&name).then_some(p)
})
+ .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
.collect();
- Ok(BasicAdtInfo { name: name_token, param_types })
+ let name_token = name_to_token(&token_map, name)?;
+ Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
}
-fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
+fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
+ let name = name.ok_or_else(|| {
+ debug!("parsed item has no name");
+ ExpandError::other("missing name")
+ })?;
+ let name_token_id =
+ token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
+ let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
+ Ok(name_token)
+}
+
+/// Given that we are deriving a trait `DerivedTrait` for a type like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
+/// a: A,
+/// b: B::Item,
+/// b1: <B as DeclaredTrait>::Item,
+/// c1: <C as WhereTrait>::Item,
+/// c2: Option<<C as WhereTrait>::Item>,
+/// ...
+/// }
+/// ```
+///
+/// create an impl like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
+/// C: WhereTrait,
+/// A: DerivedTrait + B1 + ... + BN,
+/// B: DerivedTrait + B1 + ... + BN,
+/// C: DerivedTrait + B1 + ... + BN,
+/// B::Item: DerivedTrait + B1 + ... + BN,
+/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
+/// ...
+/// {
+/// ...
+/// }
+/// ```
+///
+/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
+/// therefore does not get bound by the derived trait.
+fn expand_simple_derive(
+ tt: &tt::Subtree,
+ trait_path: tt::Subtree,
+ make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) {
Ok(info) => info,
- Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
+ let trait_body = make_trait_body(&info);
+ let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info
.param_types
.into_iter()
- .enumerate()
- .map(|(idx, param_ty)| {
- let ident = tt::Leaf::Ident(tt::Ident {
- span: tt::TokenId::unspecified(),
- text: format!("T{idx}").into(),
- });
+ .map(|(ident, param_ty, bound)| {
let ident_ = ident.clone();
+ if let Some(b) = bound {
+ let ident = ident.clone();
+ where_block.push(quote! { #ident : #b , });
+ }
if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , })
} else {
@@ -134,9 +372,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
}
})
.unzip();
+
+ where_block.extend(info.associated_types.iter().map(|x| {
+ let x = x.clone();
+ let bound = trait_path.clone();
+ quote! { #x : #bound , }
+ }));
+
let name = info.name;
let expanded = quote! {
- impl < ##params > #trait_path for #name < ##args > {}
+ impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
};
ExpandResult::ok(expanded)
}
@@ -163,7 +408,7 @@ fn copy_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::marker::Copy })
+ expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
}
fn clone_expand(
@@ -172,7 +417,63 @@ fn clone_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::clone::Clone })
+ expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn clone(&self) -> Self {
+ #star self
+ }
+ };
+ }
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn clone(&self) -> Self {
+ match #star self {}
+ }
+ };
+ }
+ let name = &adt.name;
+ let patterns = adt.shape.as_pattern(name);
+ let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() });
+ let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
+ let fat_arrow = fat_arrow();
+ quote! {
+ #pat #fat_arrow #expr,
+ }
+ });
+
+ quote! {
+ fn clone(&self) -> Self {
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
+}
+
+/// This function exists since `quote! { => }` doesn't work.
+fn fat_arrow() -> ::tt::Subtree<TokenId> {
+ let eq =
+ tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
+ quote! { #eq> }
+}
+
+/// This function exists since `quote! { && }` doesn't work.
+fn and_and() -> ::tt::Subtree<TokenId> {
+ let and =
+ tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
+ quote! { #and& }
}
fn default_expand(
@@ -180,8 +481,38 @@ fn default_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::default::Default })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| {
+ let body = match &adt.shape {
+ AdtShape::Struct(fields) => {
+ let name = &adt.name;
+ fields
+ .as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default()))
+ }
+ AdtShape::Enum { default_variant, variants } => {
+ if let Some(d) = default_variant {
+ let (name, fields) = &variants[*d];
+ let adt_name = &adt.name;
+ fields.as_pattern_map(
+ quote!(#adt_name :: #name),
+ |_| quote!(#krate::default::Default::default()),
+ )
+ } else {
+ // FIXME: Return expand error here
+ quote!()
+ }
+ }
+ AdtShape::Union => {
+ // FIXME: Return expand error here
+ quote!()
+ }
+ };
+ quote! {
+ fn default() -> Self {
+ #body
+ }
+ }
+ })
}
fn debug_expand(
@@ -189,8 +520,79 @@ fn debug_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
+ let for_variant = |name: String, v: &VariantShape| match v {
+ VariantShape::Struct(fields) => {
+ let for_fields = fields.iter().map(|x| {
+ let x_string = x.to_string();
+ quote! {
+ .field(#x_string, & #x)
+ }
+ });
+ quote! {
+ f.debug_struct(#name) ##for_fields .finish()
+ }
+ }
+ VariantShape::Tuple(n) => {
+ let for_fields = tuple_field_iterator(*n).map(|x| {
+ quote! {
+ .field( & #x)
+ }
+ });
+ quote! {
+ f.debug_tuple(#name) ##for_fields .finish()
+ }
+ }
+ VariantShape::Unit => quote! {
+ f.write_str(#name)
+ },
+ };
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
+ match #star self {}
+ }
+ };
+ }
+ let arms = match &adt.shape {
+ AdtShape::Struct(fields) => {
+ let fat_arrow = fat_arrow();
+ let name = &adt.name;
+ let pat = fields.as_pattern(quote!(#name));
+ let expr = for_variant(name.to_string(), fields);
+ vec![quote! { #pat #fat_arrow #expr }]
+ }
+ AdtShape::Enum { variants, .. } => variants
+ .iter()
+ .map(|(name, v)| {
+ let fat_arrow = fat_arrow();
+ let adt_name = &adt.name;
+ let pat = v.as_pattern(quote!(#adt_name :: #name));
+ let expr = for_variant(name.to_string(), v);
+ quote! {
+ #pat #fat_arrow #expr ,
+ }
+ })
+ .collect(),
+ AdtShape::Union => {
+ // FIXME: Return expand error here
+ vec![]
+ }
+ };
+ quote! {
+ fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
}
fn hash_expand(
@@ -198,8 +600,47 @@ fn hash_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::hash::Hash })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote! {};
+ }
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
+ match #star self {}
+ }
+ };
+ }
+ let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
+ |(pat, names)| {
+ let expr = {
+ let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); });
+ quote! { {
+ ##it
+ } }
+ };
+ let fat_arrow = fat_arrow();
+ quote! {
+ #pat #fat_arrow #expr ,
+ }
+ },
+ );
+ quote! {
+ fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
+ #krate::mem::discriminant(self).hash(ra_expand_state);
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
}
fn eq_expand(
@@ -208,7 +649,7 @@ fn eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::Eq })
+ expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
}
fn partial_eq_expand(
@@ -217,7 +658,65 @@ fn partial_eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
+ expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote! {};
+ }
+ let name = &adt.name;
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, names)| {
+ let fat_arrow = fat_arrow();
+ let body = match &*names {
+ [] => {
+ quote!(true)
+ }
+ [first, rest @ ..] => {
+ let rest = rest.iter().map(|x| {
+ let t1 = Ident::new(format!("{}_self", x.text), x.span);
+ let t2 = Ident::new(format!("{}_other", x.text), x.span);
+ let and_and = and_and();
+ quote!(#and_and #t1 .eq( #t2 ))
+ });
+ let first = {
+ let t1 = Ident::new(format!("{}_self", first.text), first.span);
+ let t2 = Ident::new(format!("{}_other", first.text), first.span);
+ quote!(#t1 .eq( #t2 ))
+ };
+ quote!(#first ##rest)
+ }
+ };
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+
+ let fat_arrow = fat_arrow();
+ quote! {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow false
+ }
+ }
+ }
+ })
+}
+
+fn self_and_other_patterns(
+ adt: &BasicAdtInfo,
+ name: &tt::Ident,
+) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
+ let self_patterns = adt.shape.as_pattern_map(name, |x| {
+ let t = Ident::new(format!("{}_self", x.text), x.span);
+ quote!(#t)
+ });
+ let other_patterns = adt.shape.as_pattern_map(name, |x| {
+ let t = Ident::new(format!("{}_other", x.text), x.span);
+ quote!(#t)
+ });
+ (self_patterns, other_patterns)
}
fn ord_expand(
@@ -225,8 +724,63 @@ fn ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
+ fn compare(
+ krate: &tt::TokenTree,
+ left: tt::Subtree,
+ right: tt::Subtree,
+ rest: tt::Subtree,
+ ) -> tt::Subtree {
+ let fat_arrow1 = fat_arrow();
+ let fat_arrow2 = fat_arrow();
+ quote! {
+ match #left.cmp(&#right) {
+ #krate::cmp::Ordering::Equal #fat_arrow1 {
+ #rest
+ }
+ c #fat_arrow2 return c,
+ }
+ }
+ }
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote!();
+ }
+ let left = quote!(#krate::intrinsics::discriminant_value(self));
+ let right = quote!(#krate::intrinsics::discriminant_value(other));
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, fields)| {
+ let mut body = quote!(#krate::cmp::Ordering::Equal);
+ for f in fields.into_iter().rev() {
+ let t1 = Ident::new(format!("{}_self", f.text), f.span);
+ let t2 = Ident::new(format!("{}_other", f.text), f.span);
+ body = compare(krate, quote!(#t1), quote!(#t2), body);
+ }
+ let fat_arrow = fat_arrow();
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+ let fat_arrow = fat_arrow();
+ let body = compare(
+ krate,
+ left,
+ right,
+ quote! {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow #krate::cmp::Ordering::Equal
+ }
+ },
+ );
+ quote! {
+ fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
+ #body
+ }
+ }
+ })
}
fn partial_ord_expand(
@@ -234,6 +788,61 @@ fn partial_ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
+ fn compare(
+ krate: &tt::TokenTree,
+ left: tt::Subtree,
+ right: tt::Subtree,
+ rest: tt::Subtree,
+ ) -> tt::Subtree {
+ let fat_arrow1 = fat_arrow();
+ let fat_arrow2 = fat_arrow();
+ quote! {
+ match #left.partial_cmp(&#right) {
+ #krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
+ #rest
+ }
+ c #fat_arrow2 return c,
+ }
+ }
+ }
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote!();
+ }
+ let left = quote!(#krate::intrinsics::discriminant_value(self));
+ let right = quote!(#krate::intrinsics::discriminant_value(other));
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, fields)| {
+ let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
+ for f in fields.into_iter().rev() {
+ let t1 = Ident::new(format!("{}_self", f.text), f.span);
+ let t2 = Ident::new(format!("{}_other", f.text), f.span);
+ body = compare(krate, quote!(#t1), quote!(#t2), body);
+ }
+ let fat_arrow = fat_arrow();
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+ let fat_arrow = fat_arrow();
+ let body = compare(
+ krate,
+ left,
+ right,
+ quote! {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
+ }
+ },
+ );
+ quote! {
+ fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
+ #body
+ }
+ }
+ })
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index a9c5e1488..a9f0c154b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,16 +1,21 @@
//! Builtin macro
+use std::mem;
+
+use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
-use mbe::{parse_exprs_with_sep, parse_to_token_tree};
+use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
+use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
};
use crate::{
- db::ExpandDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc,
+ db::ExpandDatabase, name, quote, tt, EagerCallInfo, ExpandError, ExpandResult, MacroCallId,
+ MacroCallLoc,
};
macro_rules! register_builtin {
@@ -45,7 +50,7 @@ macro_rules! register_builtin {
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
- ) -> ExpandResult<ExpandedEager> {
+ ) -> ExpandResult<tt::Subtree> {
let expander = match *self {
$( EagerExpander::$e_kind => $e_expand, )*
};
@@ -63,16 +68,9 @@ macro_rules! register_builtin {
};
}
-#[derive(Debug)]
-pub struct ExpandedEager {
- pub(crate) subtree: tt::Subtree,
- /// The included file ID of the include macro.
- pub(crate) included_file: Option<FileId>,
-}
-
-impl ExpandedEager {
- fn new(subtree: tt::Subtree) -> Self {
- ExpandedEager { subtree, included_file: None }
+impl EagerExpander {
+ pub fn is_include(&self) -> bool {
+ matches!(self, EagerExpander::Include)
}
}
@@ -90,11 +88,6 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
- (format_args, FormatArgs) => format_args_expand,
- (const_format_args, ConstFormatArgs) => format_args_expand,
- // format_args_nl only differs in that it adds a newline in the end,
- // so we use the same stub expansion for now
- (format_args_nl, FormatArgsNl) => format_args_expand,
(llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand,
@@ -106,6 +99,9 @@ register_builtin! {
(trace_macros, TraceMacros) => trace_macros_expand,
EAGER:
+ (format_args, FormatArgs) => format_args_expand,
+ (const_format_args, ConstFormatArgs) => format_args_expand,
+ (format_args_nl, FormatArgsNl) => format_args_nl_expand,
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -135,9 +131,8 @@ fn line_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
- let line_num = 0;
let expanded = quote! {
- #line_num
+ 0 as u32
};
ExpandResult::ok(expanded)
@@ -179,9 +174,8 @@ fn column_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
- let col_num = 0;
let expanded = quote! {
- #col_num
+ 0 as u32
};
ExpandResult::ok(expanded)
@@ -234,45 +228,170 @@ fn file_expand(
}
fn format_args_expand(
+ db: &dyn ExpandDatabase,
+ id: MacroCallId,
+ tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ format_args_expand_general(db, id, tt, "")
+}
+
+fn format_args_nl_expand(
+ db: &dyn ExpandDatabase,
+ id: MacroCallId,
+ tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ format_args_expand_general(db, id, tt, "\\n")
+}
+
+fn format_args_expand_general(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
+ end_string: &str,
) -> ExpandResult<tt::Subtree> {
- // We expand `format_args!("", a1, a2)` to
- // ```
- // $crate::fmt::Arguments::new_v1(&[], &[
- // $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt),
- // $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt),
- // ])
- // ```,
- // which is still not really correct, but close enough for now
- let mut args = parse_exprs_with_sep(tt, ',');
-
- if args.is_empty() {
- return ExpandResult::with_err(
- tt::Subtree::empty(),
- mbe::ExpandError::NoMatchingRule.into(),
- );
- }
- for arg in &mut args {
+ let args = parse_exprs_with_sep(tt, ',');
+
+ let expand_error =
+ ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
+
+ let mut key_args = FxHashMap::default();
+ let mut args = args.into_iter().filter_map(|mut arg| {
// Remove `key =`.
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
// but not with `==`
- if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' )
+ if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
- arg.token_trees.drain(..2);
+ let key = arg.token_trees.drain(..2).next().unwrap();
+ key_args.insert(key.to_string(), arg);
+ return None;
+ }
+ }
+ Some(arg)
+ }).collect::<Vec<_>>().into_iter();
+ // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
+ let Some(format_subtree) = args.next() else {
+ return expand_error;
+ };
+ let format_string = (|| {
+ let token_tree = format_subtree.token_trees.get(0)?;
+ match token_tree {
+ tt::TokenTree::Leaf(l) => match l {
+ tt::Leaf::Literal(l) => {
+ if let Some(mut text) = l.text.strip_prefix('r') {
+ let mut raw_sharps = String::new();
+ while let Some(t) = text.strip_prefix('#') {
+ text = t;
+ raw_sharps.push('#');
+ }
+ text =
+ text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
+ Some((text, l.span, Some(raw_sharps)))
+ } else {
+ let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
+ let span = l.span;
+ Some((text, span, None))
+ }
+ }
+ _ => None,
+ },
+ tt::TokenTree::Subtree(_) => None,
+ }
+ })();
+ let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
+ return expand_error;
+ };
+ let mut format_iter = format_string.chars().peekable();
+ let mut parts = vec![];
+ let mut last_part = String::new();
+ let mut arg_tts = vec![];
+ let mut err = None;
+ while let Some(c) = format_iter.next() {
+ // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
+ match c {
+ '{' => {
+ if format_iter.peek() == Some(&'{') {
+ format_iter.next();
+ last_part.push('{');
+ continue;
+ }
+ let mut argument = String::new();
+ while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
+ argument.push(match format_iter.next() {
+ Some(c) => c,
+ None => return expand_error,
+ });
+ }
+ let format_spec = match format_iter.next().unwrap() {
+ '}' => "".to_owned(),
+ ':' => {
+ let mut s = String::new();
+ while let Some(c) = format_iter.next() {
+ if c == '}' {
+ break;
+ }
+ s.push(c);
+ }
+ s
+ }
+ _ => unreachable!(),
+ };
+ parts.push(mem::take(&mut last_part));
+ let arg_tree = if argument.is_empty() {
+ match args.next() {
+ Some(x) => x,
+ None => {
+ err = Some(mbe::ExpandError::NoMatchingRule.into());
+ tt::Subtree::empty()
+ }
+ }
+ } else if let Some(tree) = key_args.get(&argument) {
+ tree.clone()
+ } else {
+ // FIXME: we should pick the related substring of the `_format_string_span` as the span. You
+ // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
+ let ident = Ident::new(argument, tt::TokenId::unspecified());
+ quote!(#ident)
+ };
+ let formatter = match &*format_spec {
+ "?" => quote!(::core::fmt::Debug::fmt),
+ "" => quote!(::core::fmt::Display::fmt),
+ _ => {
+ // FIXME: implement the rest and return expand error here
+ quote!(::core::fmt::Display::fmt)
+ }
+ };
+ arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), });
}
+ '}' => {
+ if format_iter.peek() == Some(&'}') {
+ format_iter.next();
+ last_part.push('}');
+ } else {
+ return expand_error;
+ }
+ }
+ _ => last_part.push(c),
}
}
- let _format_string = args.remove(0);
- let arg_tts = args.into_iter().flat_map(|arg| {
- quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
- }.token_trees);
+ last_part += end_string;
+ if !last_part.is_empty() {
+ parts.push(last_part);
+ }
+ let part_tts = parts.into_iter().map(|x| {
+ let text = if let Some(raw) = &raw_sharps {
+ format!("r{raw}\"{}\"{raw}", x).into()
+ } else {
+ format!("\"{}\"", x).into()
+ };
+ let l = tt::Literal { span: tt::TokenId::unspecified(), text };
+ quote!(#l ,)
+ });
+ let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
let expanded = quote! {
- #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
+ ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
};
- ExpandResult::ok(expanded)
+ ExpandResult { value: expanded, err }
}
fn asm_expand(
@@ -382,23 +501,23 @@ fn compile_error_expand(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let err = match &*tt.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
- Some(unquoted) => ExpandError::Other(unquoted.into()),
- None => ExpandError::Other("`compile_error!` argument must be a string".into()),
+ Some(unquoted) => ExpandError::other(unquoted),
+ None => ExpandError::other("`compile_error!` argument must be a string"),
},
- _ => ExpandError::Other("`compile_error!` argument must be a string".into()),
+ _ => ExpandError::other("`compile_error!` argument must be a string"),
};
- ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
+ ExpandResult { value: quote! {}, err: Some(err) }
}
fn concat_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut err = None;
let mut text = String::new();
for (i, mut t) in tt.token_trees.iter().enumerate() {
@@ -437,14 +556,14 @@ fn concat_expand(
}
}
}
- ExpandResult { value: ExpandedEager::new(quote!(#text)), err }
+ ExpandResult { value: quote!(#text), err }
}
fn concat_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut bytes = Vec::new();
let mut err = None;
for (i, t) in tt.token_trees.iter().enumerate() {
@@ -477,7 +596,7 @@ fn concat_bytes_expand(
}
}
let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::TokenId::unspecified() };
- ExpandResult { value: ExpandedEager::new(quote!([#ident])), err }
+ ExpandResult { value: quote!([#ident]), err }
}
fn concat_bytes_expand_subtree(
@@ -510,7 +629,7 @@ fn concat_idents_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut err = None;
let mut ident = String::new();
for (i, t) in tt.token_trees.iter().enumerate() {
@@ -525,7 +644,7 @@ fn concat_idents_expand(
}
}
let ident = tt::Ident { text: ident.into(), span: tt::TokenId::unspecified() };
- ExpandResult { value: ExpandedEager::new(quote!(#ident)), err }
+ ExpandResult { value: quote!(#ident), err }
}
fn relative_file(
@@ -538,10 +657,10 @@ fn relative_file(
let path = AnchoredPath { anchor: call_site, path: path_str };
let res = db
.resolve_path(path)
- .ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?;
+ .ok_or_else(|| ExpandError::other(format!("failed to load file `{path_str}`")))?;
// Prevent include itself
if res == call_site && !allow_recursion {
- Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
+ Err(ExpandError::other(format!("recursive inclusion of `{path_str}`")))
} else {
Ok(res)
}
@@ -560,38 +679,37 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
fn include_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
- tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
- let res = (|| {
- let path = parse_string(tt)?;
- let file_id = relative_file(db, arg_id, &path, false)?;
-
- let subtree =
- parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
- Ok((subtree, file_id))
- })();
-
- match res {
- Ok((subtree, file_id)) => {
- ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) })
- }
- Err(e) => ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- ),
+ _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ match db.include_expand(arg_id) {
+ Ok((res, _)) => ExpandResult::ok(res.0.clone()),
+ Err(e) => ExpandResult::new(tt::Subtree::empty(), e),
}
}
+pub(crate) fn include_arg_to_tt(
+ db: &dyn ExpandDatabase,
+ arg_id: MacroCallId,
+) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
+ let loc = db.lookup_intern_macro_call(arg_id);
+ let Some(EagerCallInfo {arg, arg_id: Some(arg_id), .. }) = loc.eager.as_deref() else {
+ panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
+ };
+ let path = parse_string(&arg.0)?;
+ let file_id = relative_file(db, *arg_id, &path, false)?;
+
+ let (subtree, map) =
+ parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
+ Ok((triomphe::Arc::new((subtree, map)), file_id))
+}
+
fn include_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
if let Err(e) = parse_string(tt) {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- );
+ return ExpandResult::new(tt::Subtree::empty(), e);
}
// FIXME: actually read the file here if the user asked for macro expansion
@@ -602,22 +720,17 @@ fn include_bytes_expand(
span: tt::TokenId::unspecified(),
}))],
};
- ExpandResult::ok(ExpandedEager::new(res))
+ ExpandResult::ok(res)
}
fn include_str_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let path = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
// FIXME: we're not able to read excluded files (which is most of them because
@@ -627,14 +740,14 @@ fn include_str_expand(
let file_id = match relative_file(db, arg_id, &path, true) {
Ok(file_id) => file_id,
Err(_) => {
- return ExpandResult::ok(ExpandedEager::new(quote!("")));
+ return ExpandResult::ok(quote!(""));
}
};
let text = db.file_text(file_id);
let text = &*text;
- ExpandResult::ok(ExpandedEager::new(quote!(#text)))
+ ExpandResult::ok(quote!(#text))
}
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
@@ -646,15 +759,10 @@ fn env_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let key = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
let mut err = None;
@@ -662,41 +770,34 @@ fn env_expand(
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
if key == "OUT_DIR" {
- err = Some(ExpandError::Other(
- r#"`OUT_DIR` not set, enable "build scripts" to fix"#.into(),
- ));
+ err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#));
}
// If the variable is unset, still return a dummy string to help type inference along.
// We cannot use an empty string here, because for
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
// `include!("foo.rs"), which might go to infinite loop
- "__RA_UNIMPLEMENTED__".to_string()
+ "UNRESOLVED_ENV_VAR".to_string()
});
let expanded = quote! { #s };
- ExpandResult { value: ExpandedEager::new(expanded), err }
+ ExpandResult { value: expanded, err }
}
fn option_env_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let key = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
-
+ // FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
- Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) },
+ None => quote! { ::core::option::Option::None::<&str> },
+ Some(s) => quote! { ::core::option::Option::Some(#s) },
};
- ExpandResult::ok(ExpandedEager::new(expanded))
+ ExpandResult::ok(expanded)
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 45572499e..78b2db730 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -1,22 +1,22 @@
//! Defines database & queries for macro expansion.
-use std::sync::Arc;
-
-use base_db::{salsa, SourceDatabase};
+use base_db::{salsa, Edition, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::syntax_node_to_token_tree;
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, HasAttrs, HasDocComments},
- AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
+ AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
};
+use triomphe::Arc;
use crate::{
- ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
- hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
- ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
- MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
+ ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
+ builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
+ BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult,
+ ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
+ MacroDefKind, MacroFile, ProcMacroExpander,
};
/// Total limit on the number of tokens produced by any macro invocation.
@@ -33,6 +33,8 @@ pub enum TokenExpander {
DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
/// Stuff like `line!` and `file!`.
Builtin(BuiltinFnLikeExpander),
+ /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
+ BuiltinEager(EagerExpander),
/// `global_allocator` and such.
BuiltinAttr(BuiltinAttrExpander),
/// `derive(Copy)` and such.
@@ -51,6 +53,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
+ TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into),
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
TokenExpander::ProcMacro(_) => {
@@ -66,6 +69,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
TokenExpander::Builtin(..)
+ | TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => id,
@@ -76,6 +80,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
TokenExpander::Builtin(..)
+ | TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@@ -90,12 +95,15 @@ pub trait ExpandDatabase: SourceDatabase {
/// Main public API -- parses a hir file, not caring whether it's a real
/// file or a macro expansion.
#[salsa::transparent]
- fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
+ fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
+ #[salsa::transparent]
+ fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
/// Implementation for the macro case.
+ // This query is LRU cached
fn parse_macro_expansion(
&self,
macro_file: MacroFile,
- ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
+ ) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
/// reason why we use salsa at all.
@@ -119,15 +127,27 @@ pub trait ExpandDatabase: SourceDatabase {
/// just fetches procedural ones.
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
- /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
- fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
+ /// Expand macro call to a token tree.
+ // This query is LRU cached
+ fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
+ #[salsa::invoke(crate::builtin_fn_macro::include_arg_to_tt)]
+ fn include_expand(
+ &self,
+ arg_id: MacroCallId,
+ ) -> Result<
+ (triomphe::Arc<(::tt::Subtree<::tt::TokenId>, mbe::TokenMap)>, base_db::FileId),
+ ExpandError,
+ >;
/// Special case of the previous query for procedural macros. We can't LRU
/// proc macros, since they are not deterministic in general, and
- /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
- /// heroically debugged this once!
+ /// non-determinism breaks salsa in a very, very, very bad way.
+ /// @edwin0cheng heroically debugged this once!
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
- /// Firewall query that returns the error from the `macro_expand` query.
- fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
+ /// Firewall query that returns the errors from the `parse_macro_expansion` query.
+ fn parse_macro_expansion_error(
+ &self,
+ macro_call: MacroCallId,
+ ) -> ExpandResult<Box<[SyntaxError]>>;
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
}
@@ -159,8 +179,8 @@ pub fn expand_speculative(
);
let (attr_arg, token_id) = match loc.kind {
- MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => {
- let attr = if is_derive {
+ MacroCallKind::Attr { invoc_attr_index, .. } => {
+ let attr = if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone())
} else {
@@ -236,17 +256,26 @@ pub fn expand_speculative(
}
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
- let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
- Arc::new(map)
+ Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
}
-fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
+fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
+ match file_id.repr() {
+ HirFileIdRepr::FileId(file_id) => db.parse(file_id).tree().syntax().clone(),
+ HirFileIdRepr::MacroFile(macro_file) => {
+ db.parse_macro_expansion(macro_file).value.0.syntax_node()
+ }
+ }
+}
+
+fn parse_or_expand_with_err(
+ db: &dyn ExpandDatabase,
+ file_id: HirFileId,
+) -> ExpandResult<Parse<SyntaxNode>> {
match file_id.repr() {
- HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
+ HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
HirFileIdRepr::MacroFile(macro_file) => {
- // FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
- // forgetting about parse errors.
- db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
+ db.parse_macro_expansion(macro_file).map(|(it, _)| it)
}
}
}
@@ -254,35 +283,9 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
fn parse_macro_expansion(
db: &dyn ExpandDatabase,
macro_file: MacroFile,
-) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
+) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
let _p = profile::span("parse_macro_expansion");
- let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
-
- if let Some(err) = &err {
- // Note:
- // The final goal we would like to make all parse_macro success,
- // such that the following log will not call anyway.
- let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- let node = loc.kind.to_node(db);
-
- // collect parent information for warning log
- let parents =
- std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db))
- .map(|n| format!("{:#}", n.value))
- .collect::<Vec<_>>()
- .join("\n");
-
- tracing::debug!(
- "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
- err,
- node.value,
- parents
- );
- }
- let tt = match value {
- Some(tt) => tt,
- None => return ExpandResult { value: None, err },
- };
+ let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
let expand_to = macro_expand_to(db, macro_file.macro_call_id);
@@ -291,16 +294,21 @@ fn parse_macro_expansion(
let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
- ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err }
+ ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
}
fn macro_arg(
db: &dyn ExpandDatabase,
id: MacroCallId,
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
- let arg = db.macro_arg_text(id)?;
let loc = db.lookup_intern_macro_call(id);
+ if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
+ return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
+ }
+
+ let arg = db.macro_arg_text(id)?;
+
let node = SyntaxNode::new_root(arg);
let censor = censor_for_macro_input(&loc, &node);
let mut fixups = fixup::fixup_syntax(&node);
@@ -339,7 +347,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
.map(|it| it.syntax().clone())
.collect()
}
- MacroCallKind::Attr { is_derive: true, .. } => return None,
+ MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
MacroCallKind::Attr { invoc_attr_index, .. } => {
cov_mark::hit!(attribute_macro_attr_censoring);
ast::Item::cast(node.clone())?
@@ -376,7 +384,17 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
return None;
}
}
- Some(arg.green().into())
+ if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
+ Some(
+ mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr)
+ .0
+ .syntax_node()
+ .green()
+ .into(),
+ )
+ } else {
+ Some(arg.green().into())
+ }
}
fn macro_def(
@@ -385,13 +403,14 @@ fn macro_def(
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
match id.kind {
MacroDefKind::Declarative(ast_id) => {
+ let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
let (mac, def_site_token_map) = match ast_id.to_node(db) {
ast::Macro::MacroRules(macro_rules) => {
let arg = macro_rules
.token_tree()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
- let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?;
+ let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
(mac, def_site_token_map)
}
ast::Macro::MacroDef(macro_def) => {
@@ -399,7 +418,7 @@ fn macro_def(
.body()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
- let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?;
+ let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
(mac, def_site_token_map)
}
};
@@ -412,82 +431,98 @@ fn macro_def(
MacroDefKind::BuiltInDerive(expander, _) => {
Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
}
- MacroDefKind::BuiltInEager(..) => {
- // FIXME: Return a random error here just to make the types align.
- // This obviously should do something real instead.
- Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into()))
+ MacroDefKind::BuiltInEager(expander, ..) => {
+ Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
}
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
}
}
-fn macro_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
-) -> ExpandResult<Option<Arc<tt::Subtree>>> {
+fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
let _p = profile::span("macro_expand");
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- if let Some(eager) = &loc.eager {
- return ExpandResult {
- value: Some(eager.arg_or_expansion.clone()),
- // FIXME: There could be errors here!
- err: None,
- };
+ let loc = db.lookup_intern_macro_call(id);
+ if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
+ // This is an input expansion for an eager macro. These are already pre-expanded
+ return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
}
-
- let macro_arg = match db.macro_arg(id) {
- Some(it) => it,
- None => {
- return ExpandResult::only_err(ExpandError::Other(
- "Failed to lower macro args to token tree".into(),
- ))
- }
- };
-
let expander = match db.macro_def(loc.def) {
Ok(it) => it,
- // FIXME: This is weird -- we effectively report macro *definition*
- // errors lazily, when we try to expand the macro. Instead, they should
- // be reported at the definition site (when we construct a def map).
+ // FIXME: We should make sure to enforce a variant that invalid macro
+ // definitions do not get expanders that could reach this call path!
Err(err) => {
- return ExpandResult::only_err(ExpandError::Other(
- format!("invalid macro definition: {err}").into(),
- ))
+ return ExpandResult {
+ value: Arc::new(tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: vec![],
+ }),
+ err: Some(ExpandError::other(format!("invalid macro definition: {err}"))),
+ }
}
};
- let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
+ let Some(macro_arg) = db.macro_arg(id) else {
+ return ExpandResult {
+ value: Arc::new(
+ tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: Vec::new(),
+ },
+ ),
+ // FIXME: We should make sure to enforce a variant that invalid macro
+ // calls do not reach this call path!
+ err: Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ };
+ };
+ let (arg_tt, arg_tm, undo_info) = &*macro_arg;
+ let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt);
+
+ if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
+ // FIXME: We should report both errors!
+ err = error.clone().or(err);
+ }
+
// Set a hard limit for the expanded tt
let count = tt.count();
if TOKEN_LIMIT.check(count).is_err() {
- return ExpandResult::only_err(ExpandError::Other(
- format!(
+ return ExpandResult {
+ value: Arc::new(tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: vec![],
+ }),
+ err: Some(ExpandError::other(format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,
TOKEN_LIMIT.inner(),
- )
- .into(),
- ));
+ ))),
+ };
}
- fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
+ fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
- ExpandResult { value: Some(Arc::new(tt)), err }
+ ExpandResult { value: Arc::new(tt), err }
}
-fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
- db.macro_expand(macro_call).err
+fn parse_macro_expansion_error(
+ db: &dyn ExpandDatabase,
+ macro_call_id: MacroCallId,
+) -> ExpandResult<Box<[SyntaxError]>> {
+ db.parse_macro_expansion(MacroFile { macro_call_id })
+ .map(|it| it.0.errors().to_vec().into_boxed_slice())
}
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- let macro_arg = match db.macro_arg(id) {
- Some(it) => it,
- None => {
- return ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::Other("No arguments for proc-macro".into()),
- )
- }
+ let loc = db.lookup_intern_macro_call(id);
+ let Some(macro_arg) = db.macro_arg(id) else {
+ return ExpandResult {
+ value: tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: Vec::new(),
+ },
+ err: Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ };
};
let expander = match loc.def.kind {
@@ -512,8 +547,7 @@ fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<HygieneFram
}
fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- loc.kind.expand_to()
+ db.lookup_intern_macro_call(id).expand_to()
}
fn token_tree_to_syntax_node(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index aca41b11f..7ee3fd375 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -18,10 +18,9 @@
//!
//!
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
-use std::sync::Arc;
-
use base_db::CrateId;
-use syntax::{ted, SyntaxNode};
+use syntax::{ted, Parse, SyntaxNode};
+use triomphe::Arc;
use crate::{
ast::{self, AstNode},
@@ -32,143 +31,77 @@ use crate::{
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
};
-#[derive(Debug)]
-pub struct ErrorEmitted {
- _private: (),
-}
-
-pub trait ErrorSink {
- fn emit(&mut self, err: ExpandError);
-
- fn option<T>(
- &mut self,
- opt: Option<T>,
- error: impl FnOnce() -> ExpandError,
- ) -> Result<T, ErrorEmitted> {
- match opt {
- Some(it) => Ok(it),
- None => {
- self.emit(error());
- Err(ErrorEmitted { _private: () })
- }
- }
- }
-
- fn option_with<T>(
- &mut self,
- opt: impl FnOnce() -> Option<T>,
- error: impl FnOnce() -> ExpandError,
- ) -> Result<T, ErrorEmitted> {
- self.option(opt(), error)
- }
-
- fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
- match res {
- Ok(it) => Ok(it),
- Err(e) => {
- self.emit(e);
- Err(ErrorEmitted { _private: () })
- }
- }
- }
-
- fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
- match (res.value, res.err) {
- (None, Some(err)) => {
- self.emit(err);
- Err(ErrorEmitted { _private: () })
- }
- (Some(value), opt_err) => {
- if let Some(err) = opt_err {
- self.emit(err);
- }
- Ok(value)
- }
- (None, None) => unreachable!("`ExpandResult` without value or error"),
- }
- }
-}
-
-impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
- fn emit(&mut self, err: ExpandError) {
- self(err);
- }
-}
-
-pub fn expand_eager_macro(
+pub fn expand_eager_macro_input(
db: &dyn ExpandDatabase,
krate: CrateId,
macro_call: InFile<ast::MacroCall>,
def: MacroDefId,
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
- diagnostic_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
- let hygiene = Hygiene::new(db, macro_call.file_id);
- let parsed_args = macro_call
- .value
- .token_tree()
- .map(|tt| mbe::syntax_node_to_token_tree(tt.syntax()).0)
- .unwrap_or_else(tt::Subtree::empty);
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
+ assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
+ let token_tree = macro_call.value.token_tree();
+
+ let Some(token_tree) = token_tree else {
+ return Ok(ExpandResult { value: None, err:
+ Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ });
+ };
+ let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
let ast_map = db.ast_id_map(macro_call.file_id);
let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value));
let expand_to = ExpandTo::from_call_site(&macro_call.value);
// Note:
- // When `lazy_expand` is called, its *parent* file must be already exists.
- // Here we store an eager macro id for the argument expanded subtree here
+ // When `lazy_expand` is called, its *parent* file must already exist.
+ // Here we store an eager macro id for the argument expanded subtree
// for that purpose.
let arg_id = db.intern_macro_call(MacroCallLoc {
def,
krate,
- eager: Some(EagerCallInfo {
- arg_or_expansion: Arc::new(parsed_args.clone()),
- included_file: None,
- }),
+ eager: Some(Box::new(EagerCallInfo {
+ arg: Arc::new((parsed_args, arg_token_map)),
+ arg_id: None,
+ error: None,
+ })),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
});
-
- let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
- let result = match eager_macro_recur(
+ let arg_as_expr = match db.macro_arg_text(arg_id) {
+ Some(it) => it,
+ None => {
+ return Ok(ExpandResult {
+ value: None,
+ err: Some(ExpandError::other("invalid token tree")),
+ })
+ }
+ };
+ let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
db,
- &hygiene,
- InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
+ &Hygiene::new(db, macro_call.file_id),
+ InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
krate,
resolver,
- diagnostic_sink,
- ) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
- Err(err) => return Err(err),
+ )?;
+ let Some(expanded_eager_input) = expanded_eager_input else {
+ return Ok(ExpandResult { value: None, err })
};
- let subtree = to_subtree(&result);
-
- if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
- let res = eager.expand(db, arg_id, &subtree);
- if let Some(err) = res.err {
- diagnostic_sink(err);
- }
-
- let loc = MacroCallLoc {
- def,
- krate,
- eager: Some(EagerCallInfo {
- arg_or_expansion: Arc::new(res.value.subtree),
- included_file: res.value.included_file,
- }),
- kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
- };
+ let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
+ subtree.delimiter = crate::tt::Delimiter::unspecified();
- Ok(Ok(db.intern_macro_call(loc)))
- } else {
- panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
- }
-}
+ let loc = MacroCallLoc {
+ def,
+ krate,
+ eager: Some(Box::new(EagerCallInfo {
+ arg: Arc::new((subtree, token_map)),
+ arg_id: Some(arg_id),
+ error: err.clone(),
+ })),
+ kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
+ };
-fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
- let mut subtree = mbe::syntax_node_to_token_tree(node).0;
- subtree.delimiter = crate::tt::Delimiter::unspecified();
- subtree
+ Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
}
fn lazy_expand(
@@ -176,7 +109,7 @@ fn lazy_expand(
def: &MacroDefId,
macro_call: InFile<ast::MacroCall>,
krate: CrateId,
-) -> ExpandResult<Option<InFile<SyntaxNode>>> {
+) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_call.value);
@@ -186,10 +119,9 @@ fn lazy_expand(
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
);
- let err = db.macro_expand_error(id);
- let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
+ let macro_file = id.as_macro_file();
- ExpandResult { value, err }
+ db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
}
fn eager_macro_recur(
@@ -198,69 +130,83 @@ fn eager_macro_recur(
curr: InFile<SyntaxNode>,
krate: CrateId,
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
- mut diagnostic_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
let original = curr.value.clone_for_update();
let children = original.descendants().filter_map(ast::MacroCall::cast);
let mut replacements = Vec::new();
+ // Note: We only report a single error inside of eager expansions
+ let mut error = None;
+
// Collect replacement
for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => {
- diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
+ error = Some(ExpandError::other("malformed macro invocation"));
continue;
}
};
- let insert = match def.kind {
+ let ExpandResult { value, err } = match def.kind {
MacroDefKind::BuiltInEager(..) => {
- let id = match expand_eager_macro(
+ let ExpandResult { value, err } = match expand_eager_macro_input(
db,
krate,
curr.with_value(child.clone()),
def,
macro_resolver,
- diagnostic_sink,
) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
+ Ok(it) => it,
Err(err) => return Err(err),
};
- db.parse_or_expand(id.as_file())
- .expect("successful macro expansion should be parseable")
- .clone_for_update()
+ match value {
+ Some(call) => {
+ let ExpandResult { value, err: err2 } =
+ db.parse_macro_expansion(call.as_macro_file());
+ ExpandResult {
+ value: Some(value.0.syntax_node().clone_for_update()),
+ err: err.or(err2),
+ }
+ }
+ None => ExpandResult { value: None, err },
+ }
}
MacroDefKind::Declarative(_)
| MacroDefKind::BuiltIn(..)
| MacroDefKind::BuiltInAttr(..)
| MacroDefKind::BuiltInDerive(..)
| MacroDefKind::ProcMacro(..) => {
- let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
- let val = match diagnostic_sink.expand_result_option(res) {
- Ok(it) => it,
- Err(err) => return Ok(Err(err)),
- };
+ let ExpandResult { value, err } =
+ lazy_expand(db, &def, curr.with_value(child.clone()), krate);
// replace macro inside
- let hygiene = Hygiene::new(db, val.file_id);
- match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
- Err(err) => return Err(err),
- }
+ let hygiene = Hygiene::new(db, value.file_id);
+ let ExpandResult { value, err: error } = eager_macro_recur(
+ db,
+ &hygiene,
+ // FIXME: We discard parse errors here
+ value.map(|it| it.syntax_node()),
+ krate,
+ macro_resolver,
+ )?;
+ let err = err.or(error);
+ ExpandResult { value, err }
}
};
-
+ if err.is_some() {
+ error = err;
+ }
// check if the whole original syntax is replaced
if child.syntax() == &original {
- return Ok(Ok(insert));
+ return Ok(ExpandResult { value, err: error });
}
- replacements.push((child, insert));
+ if let Some(insert) = value {
+ replacements.push((child, insert));
+ }
}
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
- Ok(Ok(original))
+ Ok(ExpandResult { value: Some(original), err: error })
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index b273f2176..00796e7c0 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -14,7 +14,7 @@ use tt::token_id::Subtree;
/// The result of calculating fixes for a syntax node -- a bunch of changes
/// (appending to and replacing nodes), the information that is needed to
/// reverse those changes afterwards, and a token map.
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub(crate) struct SyntaxFixups {
pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
pub(crate) replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
@@ -24,7 +24,7 @@ pub(crate) struct SyntaxFixups {
}
/// This is the information needed to reverse the fixups.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Default, PartialEq, Eq)]
pub struct SyntaxFixupUndoInfo {
original: Vec<Subtree>,
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index 2eb56fc9e..10f8fe9ce 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -2,8 +2,6 @@
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
-use std::sync::Arc;
-
use base_db::CrateId;
use db::TokenExpander;
use either::Either;
@@ -12,6 +10,7 @@ use syntax::{
ast::{self, HasDocComments},
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::{self, ExpandDatabase},
@@ -200,8 +199,14 @@ fn make_hygiene_info(
});
let macro_def = db.macro_def(loc.def).ok()?;
- let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
- let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
+ let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
+ let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
+ Arc::new((
+ tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
+ Default::default(),
+ Default::default(),
+ ))
+ });
Some(HygieneInfo {
file: macro_file,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 5e99eacc1..e0c199328 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -20,11 +20,13 @@ pub mod mod_path;
pub mod attrs;
mod fixup;
+use mbe::TokenMap;
pub use mbe::{Origin, ValueResult};
use ::tt::token_id as tt;
+use triomphe::Arc;
-use std::{fmt, hash::Hash, iter, sync::Arc};
+use std::{fmt, hash::Hash, iter};
use base_db::{
impl_intern_key,
@@ -51,12 +53,18 @@ use crate::{
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError),
- RecursionOverflowPosioned,
- Other(Box<str>),
+ RecursionOverflowPoisoned,
+ Other(Box<Box<str>>),
+}
+
+impl ExpandError {
+ pub fn other(msg: impl Into<Box<str>>) -> Self {
+ ExpandError::Other(Box::new(msg.into()))
+ }
}
impl From<mbe::ExpandError> for ExpandError {
@@ -70,7 +78,7 @@ impl fmt::Display for ExpandError {
match self {
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
ExpandError::Mbe(it) => it.fmt(f),
- ExpandError::RecursionOverflowPosioned => {
+ ExpandError::RecursionOverflowPoisoned => {
f.write_str("overflow expanding the original macro")
}
ExpandError::Other(it) => f.write_str(it),
@@ -95,9 +103,15 @@ impl fmt::Display for ExpandError {
/// The two variants are encoded in a single u32 which are differentiated by the MSB.
/// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
/// `MacroCallId`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct HirFileId(u32);
+impl fmt::Debug for HirFileId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.repr().fmt(f)
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroFile {
pub macro_call_id: MacroCallId,
@@ -113,7 +127,8 @@ impl_intern_key!(MacroCallId);
pub struct MacroCallLoc {
pub def: MacroDefId,
pub(crate) krate: CrateId,
- eager: Option<EagerCallInfo>,
+ /// Some if `def` is a builtin eager macro.
+ eager: Option<Box<EagerCallInfo>>,
pub kind: MacroCallKind,
}
@@ -138,8 +153,11 @@ pub enum MacroDefKind {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct EagerCallInfo {
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
- arg_or_expansion: Arc<tt::Subtree>,
- included_file: Option<FileId>,
+ arg: Arc<(tt::Subtree, TokenMap)>,
+ /// call id of the eager macro's input file. If this is none, macro call containing this call info
+ /// is an eager macro's input, otherwise it is its output.
+ arg_id: Option<MacroCallId>,
+ error: Option<ExpandError>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -166,8 +184,6 @@ pub enum MacroCallKind {
/// Outer attributes are counted first, then inner attributes. This does not support
/// out-of-line modules, which may have attributes spread across 2 files!
invoc_attr_index: AttrId,
- /// Whether this attribute is the `#[derive]` attribute.
- is_derive: bool,
},
}
@@ -205,10 +221,15 @@ impl HirFileId {
HirFileIdRepr::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
- file_id = match loc.eager {
- Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
+ let is_include_expansion = loc.def.is_include()
+ && matches!(
+ loc.eager.as_deref(),
+ Some(EagerCallInfo { arg_id: Some(_), .. })
+ );
+ file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
+ Some(Ok((_, file))) => file.into(),
_ => loc.kind.file_id(),
- };
+ }
}
}
}
@@ -230,18 +251,17 @@ impl HirFileId {
pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- Some(loc.kind.to_node(db))
+ Some(loc.to_node(db))
}
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
- let mut call =
- db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
+ let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
loop {
match call.file_id.repr() {
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
- call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
+ call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
}
}
}
@@ -255,8 +275,14 @@ impl HirFileId {
let arg_tt = loc.kind.arg(db)?;
let macro_def = db.macro_def(loc.def).ok()?;
- let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
- let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
+ let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
+ let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
+ Arc::new((
+ tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
+ Default::default(),
+ Default::default(),
+ ))
+ });
let def = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) {
@@ -298,7 +324,7 @@ impl HirFileId {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let attr = match loc.def.kind {
- MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
+ MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
_ => return None,
};
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
@@ -319,7 +345,17 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
+ loc.def.is_include()
+ }
+ _ => false,
+ }
+ }
+
+ pub fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
+ match self.macro_file() {
+ Some(macro_file) => {
+ let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+ matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
}
_ => false,
}
@@ -342,7 +378,7 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
+ loc.def.is_attribute_derive()
}
None => false,
}
@@ -413,22 +449,19 @@ impl MacroDefId {
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
)
}
-}
-// FIXME: attribute indices do not account for nested `cfg_attr`
+ pub fn is_attribute_derive(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
+ }
-impl MacroCallKind {
- /// Returns the file containing the macro invocation.
- fn file_id(&self) -> HirFileId {
- match *self {
- MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
- | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
- | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
- }
+ pub fn is_include(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
}
+}
+impl MacroCallLoc {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
- match self {
+ match self.kind {
MacroCallKind::FnLike { ast_id, .. } => {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
@@ -444,23 +477,49 @@ impl MacroCallKind {
.unwrap_or_else(|| it.syntax().clone())
})
}
- MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => {
- // FIXME: handle `cfg_attr`
- ast_id.with_value(ast_id.to_node(db)).map(|it| {
- it.doc_comments_and_attrs()
- .nth(invoc_attr_index.ast_index())
- .and_then(|it| match it {
- Either::Left(attr) => Some(attr.syntax().clone()),
- Either::Right(_) => None,
- })
- .unwrap_or_else(|| it.syntax().clone())
- })
+ MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+ if self.def.is_attribute_derive() {
+ // FIXME: handle `cfg_attr`
+ ast_id.with_value(ast_id.to_node(db)).map(|it| {
+ it.doc_comments_and_attrs()
+ .nth(invoc_attr_index.ast_index())
+ .and_then(|it| match it {
+ Either::Left(attr) => Some(attr.syntax().clone()),
+ Either::Right(_) => None,
+ })
+ .unwrap_or_else(|| it.syntax().clone())
+ })
+ } else {
+ ast_id.with_value(ast_id.to_node(db).syntax().clone())
+ }
}
- MacroCallKind::Attr { ast_id, .. } => {
- ast_id.with_value(ast_id.to_node(db).syntax().clone())
+ }
+ }
+
+ fn expand_to(&self) -> ExpandTo {
+ match self.kind {
+ MacroCallKind::FnLike { expand_to, .. } => expand_to,
+ MacroCallKind::Derive { .. } => ExpandTo::Items,
+ MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
+ MacroCallKind::Attr { .. } => {
+ // is this always correct?
+ ExpandTo::Items
}
}
}
+}
+
+// FIXME: attribute indices do not account for nested `cfg_attr`
+
+impl MacroCallKind {
+ /// Returns the file containing the macro invocation.
+ fn file_id(&self) -> HirFileId {
+ match *self {
+ MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
+ | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
+ | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
+ }
+ }
/// Returns the original file range that best describes the location of this macro call.
///
@@ -538,21 +597,16 @@ impl MacroCallKind {
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
}
}
-
- fn expand_to(&self) -> ExpandTo {
- match self {
- MacroCallKind::FnLike { expand_to, .. } => *expand_to,
- MacroCallKind::Derive { .. } => ExpandTo::Items,
- MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
- MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
- }
- }
}
impl MacroCallId {
pub fn as_file(self) -> HirFileId {
MacroFile { macro_call_id: self }.into()
}
+
+ pub fn as_macro_file(self) -> MacroFile {
+ MacroFile { macro_call_id: self }
+ }
}
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
@@ -610,7 +664,7 @@ impl ExpansionInfo {
let token_range = token.value.text_range();
match &loc.kind {
- MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
+ MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr`
let attr = item
.doc_comments_and_attrs()
@@ -626,7 +680,8 @@ impl ExpansionInfo {
token.value.text_range().checked_sub(attr_input_start)?;
// shift by the item's tree's max id
let token_id = attr_args.1.token_by_range(relative_range)?;
- let token_id = if *is_derive {
+
+ let token_id = if loc.def.is_attribute_derive() {
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
token_id
} else {
@@ -645,7 +700,7 @@ impl ExpansionInfo {
let token_id = match token_id_in_attr_input {
Some(token_id) => token_id,
- // the token is not inside an attribute's input so do the lookup in the macro_arg as usual
+ // the token is not inside `an attribute's input so do the lookup in the macro_arg as usual
None => {
let relative_range =
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
@@ -677,20 +732,35 @@ impl ExpansionInfo {
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
let loc = db.lookup_intern_macro_call(call_id);
+ // Special case: map tokens from `include!` expansions to the included file
+ if loc.def.is_include()
+ && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
+ {
+ if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
+ let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
+ let source = db.parse(file_id);
+
+ let token = source.syntax_node().covering_element(range).into_token()?;
+
+ return Some((InFile::new(file_id.into(), token), Origin::Call));
+ }
+ }
+
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
let (token_map, tt) = match &loc.kind {
- MacroCallKind::Attr { attr_args, is_derive: true, .. } => {
- (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
- }
MacroCallKind::Attr { attr_args, .. } => {
- // try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
- // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
- match self.macro_arg_shift.unshift(token_id) {
- Some(unshifted) => {
- token_id = unshifted;
- (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ if loc.def.is_attribute_derive() {
+ (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ } else {
+ // try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
+ // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
+ match self.macro_arg_shift.unshift(token_id) {
+ Some(unshifted) => {
+ token_id = unshifted;
+ (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ }
+ None => (&self.macro_arg.1, self.arg.clone()),
}
- None => (&self.macro_arg.1, self.arg.clone()),
}
}
_ => match origin {
@@ -718,7 +788,7 @@ pub type AstId<N> = InFile<FileAstId<N>>;
impl<N: AstNode> AstId<N> {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
- let root = db.parse_or_expand(self.file_id).unwrap();
+ let root = db.parse_or_expand(self.file_id);
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
}
}
@@ -754,7 +824,7 @@ impl<T> InFile<T> {
}
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- db.parse_or_expand(self.file_id).expect("source created from invalid file")
+ db.parse_or_expand(self.file_id)
}
}
@@ -950,6 +1020,7 @@ fn ascend_node_border_tokens(
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
+ // FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
let first = first_token(node)?;
let last = last_token(node)?;
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
@@ -977,6 +1048,7 @@ impl<N: AstNode> InFile<N> {
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
}
+ // FIXME: this should return `Option<InFileNotHirFile<N>>`
pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>> {
// This kind of upmapping can only be achieved in attribute expanded files,
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index e9393cc89..47a8ab7de 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -1,7 +1,7 @@
//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
use std::{
- fmt::{self, Display},
+ fmt::{self, Display as _},
iter,
};
@@ -24,6 +24,12 @@ pub struct ModPath {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnescapedModPath<'a>(&'a ModPath);
+impl<'a> UnescapedModPath<'a> {
+ pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ UnescapedDisplay { db, path: self }
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind {
Plain,
@@ -110,52 +116,30 @@ impl ModPath {
UnescapedModPath(self)
}
- fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
- let mut first_segment = true;
- let mut add_segment = |s| -> fmt::Result {
- if !first_segment {
- f.write_str("::")?;
- }
- first_segment = false;
- f.write_str(s)?;
- Ok(())
- };
- match self.kind {
- PathKind::Plain => {}
- PathKind::Super(0) => add_segment("self")?,
- PathKind::Super(n) => {
- for _ in 0..n {
- add_segment("super")?;
- }
- }
- PathKind::Crate => add_segment("crate")?,
- PathKind::Abs => add_segment("")?,
- PathKind::DollarCrate(_) => add_segment("$crate")?,
- }
- for segment in &self.segments {
- if !first_segment {
- f.write_str("::")?;
- }
- first_segment = false;
- if escaped {
- segment.fmt(f)?
- } else {
- segment.unescaped().fmt(f)?
- };
- }
- Ok(())
+ pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ Display { db, path: self }
}
}
-impl Display for ModPath {
+struct Display<'a> {
+ db: &'a dyn ExpandDatabase,
+ path: &'a ModPath,
+}
+
+impl<'a> fmt::Display for Display<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self._fmt(f, true)
+ display_fmt_path(self.db, self.path, f, true)
}
}
-impl<'a> Display for UnescapedModPath<'a> {
+struct UnescapedDisplay<'a> {
+ db: &'a dyn ExpandDatabase,
+ path: &'a UnescapedModPath<'a>,
+}
+
+impl<'a> fmt::Display for UnescapedDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0._fmt(f, false)
+ display_fmt_path(self.db, self.path.0, f, false)
}
}
@@ -164,6 +148,46 @@ impl From<Name> for ModPath {
ModPath::from_segments(PathKind::Plain, iter::once(name))
}
}
+fn display_fmt_path(
+ db: &dyn ExpandDatabase,
+ path: &ModPath,
+ f: &mut fmt::Formatter<'_>,
+ escaped: bool,
+) -> fmt::Result {
+ let mut first_segment = true;
+ let mut add_segment = |s| -> fmt::Result {
+ if !first_segment {
+ f.write_str("::")?;
+ }
+ first_segment = false;
+ f.write_str(s)?;
+ Ok(())
+ };
+ match path.kind {
+ PathKind::Plain => {}
+ PathKind::Super(0) => add_segment("self")?,
+ PathKind::Super(n) => {
+ for _ in 0..n {
+ add_segment("super")?;
+ }
+ }
+ PathKind::Crate => add_segment("crate")?,
+ PathKind::Abs => add_segment("")?,
+ PathKind::DollarCrate(_) => add_segment("$crate")?,
+ }
+ for segment in &path.segments {
+ if !first_segment {
+ f.write_str("::")?;
+ }
+ first_segment = false;
+ if escaped {
+ segment.display(db).fmt(f)?;
+ } else {
+ segment.unescaped().display(db).fmt(f)?;
+ }
+ }
+ Ok(())
+}
fn convert_path(
db: &dyn ExpandDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index c3462beac..f8dbb8427 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -24,27 +24,6 @@ enum Repr {
TupleField(usize),
}
-impl fmt::Display for Name {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match &self.0 {
- Repr::Text(text) => fmt::Display::fmt(&text, f),
- Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
- }
- }
-}
-
-impl<'a> fmt::Display for UnescapedName<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match &self.0 .0 {
- Repr::Text(text) => {
- let text = text.strip_prefix("r#").unwrap_or(text);
- fmt::Display::fmt(&text, f)
- }
- Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
- }
- }
-}
-
impl<'a> UnescapedName<'a> {
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
@@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> {
Repr::TupleField(it) => SmolStr::new(it.to_string()),
}
}
+
+ pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ _ = db;
+ UnescapedDisplay { name: self }
+ }
}
impl Name {
@@ -78,7 +62,7 @@ impl Name {
Self::new_text(lt.text().into())
}
- /// Shortcut to create inline plain text name
+ /// Shortcut to create inline plain text name. Panics if `text.len() > 22`
const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text))
}
@@ -112,6 +96,17 @@ impl Name {
Name::new_inline("[missing name]")
}
+ /// Generates a new name which is only equal to itself, by incrementing a counter. Due
+ /// its implementation, it should not be used in things that salsa considers, like
+ /// type names or field names, and it should be only used in names of local variables
+ /// and labels and similar things.
+ pub fn generate_new_name() -> Name {
+ use std::sync::atomic::{AtomicUsize, Ordering};
+ static CNT: AtomicUsize = AtomicUsize::new(0);
+ let c = CNT.fetch_add(1, Ordering::Relaxed);
+ Name::new_text(format!("<ra@gennew>{c}").into())
+ }
+
/// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
@@ -156,6 +151,40 @@ impl Name {
Repr::TupleField(_) => false,
}
}
+
+ pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ _ = db;
+ Display { name: self }
+ }
+}
+
+struct Display<'a> {
+ name: &'a Name,
+}
+
+impl<'a> fmt::Display for Display<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match &self.name.0 {
+ Repr::Text(text) => fmt::Display::fmt(&text, f),
+ Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
+ }
+ }
+}
+
+struct UnescapedDisplay<'a> {
+ name: &'a UnescapedName<'a>,
+}
+
+impl<'a> fmt::Display for UnescapedDisplay<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match &self.name.0 .0 {
+ Repr::Text(text) => {
+ let text = text.strip_prefix("r#").unwrap_or(text);
+ fmt::Display::fmt(&text, f)
+ }
+ Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
+ }
+ }
}
pub trait AsName {
@@ -337,18 +366,24 @@ pub mod known {
crate_type,
derive,
global_allocator,
+ no_core,
+ no_std,
test,
test_case,
recursion_limit,
feature,
// known methods of lang items
call_once,
+ call_mut,
+ call,
eq,
ne,
ge,
gt,
le,
lt,
+ // known fields of lang items
+ pieces,
// lang items
add_assign,
add,
@@ -363,6 +398,7 @@ pub mod known {
deref,
div_assign,
div,
+ drop,
fn_mut,
fn_once,
future_trait,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index d758e9302..41675c630 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander {
- proc_macro_id: Option<ProcMacroId>,
+ proc_macro_id: ProcMacroId,
}
+const DUMMY_ID: u32 = !0;
+
impl ProcMacroExpander {
pub fn new(proc_macro_id: ProcMacroId) -> Self {
- Self { proc_macro_id: Some(proc_macro_id) }
+ assert_ne!(proc_macro_id.0, DUMMY_ID);
+ Self { proc_macro_id }
}
pub fn dummy() -> Self {
- Self { proc_macro_id: None }
+ Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
}
pub fn is_dummy(&self) -> bool {
- self.proc_macro_id.is_none()
+ self.proc_macro_id.0 == DUMMY_ID
}
pub fn expand(
@@ -32,33 +35,37 @@ impl ProcMacroExpander {
attr_arg: Option<&tt::Subtree>,
) -> ExpandResult<tt::Subtree> {
match self.proc_macro_id {
- Some(id) => {
- let krate_graph = db.crate_graph();
- let proc_macros = match &krate_graph[def_crate].proc_macro {
- Ok(proc_macros) => proc_macros,
- Err(_) => {
+ ProcMacroId(DUMMY_ID) => {
+ ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
+ }
+ ProcMacroId(id) => {
+ let proc_macros = db.proc_macros();
+ let proc_macros = match proc_macros.get(&def_crate) {
+ Some(Ok(proc_macros)) => proc_macros,
+ Some(Err(_)) | None => {
never!("Non-dummy expander even though there are no proc macros");
- return ExpandResult::with_err(
+ return ExpandResult::new(
tt::Subtree::empty(),
- ExpandError::Other("Internal error".into()),
+ ExpandError::other("Internal error"),
);
}
};
- let proc_macro = match proc_macros.get(id.0 as usize) {
+ let proc_macro = match proc_macros.get(id as usize) {
Some(proc_macro) => proc_macro,
None => {
never!(
"Proc macro index out of bounds: the length is {} but the index is {}",
proc_macros.len(),
- id.0
+ id
);
- return ExpandResult::with_err(
+ return ExpandResult::new(
tt::Subtree::empty(),
- ExpandError::Other("Internal error".into()),
+ ExpandError::other("Internal error"),
);
}
};
+ let krate_graph = db.crate_graph();
// Proc macros have access to the environment variables of the invoking crate.
let env = &krate_graph[calling_crate].env;
match proc_macro.expander.expand(tt, attr_arg, env) {
@@ -68,23 +75,15 @@ impl ProcMacroExpander {
ProcMacroExpansionError::System(text)
if proc_macro.kind == ProcMacroKind::Attr =>
{
- ExpandResult {
- value: tt.clone(),
- err: Some(ExpandError::Other(text.into())),
- }
+ ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
}
ProcMacroExpansionError::System(text)
- | ProcMacroExpansionError::Panic(text) => ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::Other(text.into()),
- ),
+ | ProcMacroExpansionError::Panic(text) => {
+ ExpandResult::new(tt::Subtree::empty(), ExpandError::other(text))
+ }
},
}
}
- None => ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::UnresolvedProcMacro(def_crate),
- ),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
index 63586f9da..ab3809abc 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
@@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree {
}
}
+impl ToTokenTree for &crate::tt::TokenTree {
+ fn to_token(self) -> crate::tt::TokenTree {
+ self.clone()
+ }
+}
+
impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree {
self.into()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 9b3296df2..c8bea3450 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -15,19 +15,21 @@ doctest = false
cov-mark = "2.0.0-pre.1"
itertools = "0.10.5"
arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
smallvec.workspace = true
ena = "0.14.0"
either = "1.7.0"
tracing = "0.1.35"
rustc-hash = "1.1.0"
scoped-tls = "1.0.0"
-chalk-solve = { version = "0.89.0", default-features = false }
-chalk-ir = "0.89.0"
-chalk-recursive = { version = "0.89.0", default-features = false }
-chalk-derive = "0.89.0"
+chalk-solve = { version = "0.91.0", default-features = false }
+chalk-ir = "0.91.0"
+chalk-recursive = { version = "0.91.0", default-features = false }
+chalk-derive = "0.91.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.17.0"
+triomphe.workspace = true
+nohash-hasher.workspace = true
typed-arena = "2.0.1"
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index 58744dd0c..3860bccec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -3,12 +3,11 @@
//! reference to a type with the field `bar`. This is an approximation of the
//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
-use std::sync::Arc;
-
use chalk_ir::cast::Cast;
use hir_def::lang_item::LangItem;
use hir_expand::name::name;
use limit::Limit;
+use triomphe::Arc;
use crate::{
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
@@ -23,6 +22,41 @@ pub(crate) enum AutoderefKind {
Overloaded,
}
+/// Returns types that `ty` transitively dereferences to. This function is only meant to be used
+/// outside `hir-ty`.
+///
+/// It is guaranteed that:
+/// - the yielded types don't contain inference variables (but may contain `TyKind::Error`).
+/// - a type won't be yielded more than once; in other words, the returned iterator will stop if it
+/// detects a cycle in the deref chain.
+pub fn autoderef(
+ db: &dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
+ ty: Canonical<Ty>,
+) -> impl Iterator<Item = Ty> {
+ let mut table = InferenceTable::new(db, env);
+ let ty = table.instantiate_canonical(ty);
+ let mut autoderef = Autoderef::new(&mut table, ty);
+ let mut v = Vec::new();
+ while let Some((ty, _steps)) = autoderef.next() {
+ // `ty` may contain unresolved inference variables. Since there's no chance they would be
+ // resolved, just replace with fallback type.
+ let resolved = autoderef.table.resolve_completely(ty);
+
+ // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
+ // would revisit some already visited types. Stop here to avoid duplication.
+ //
+ // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
+ // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
+ // performant.
+ if v.contains(&resolved) {
+ break;
+ }
+ v.push(resolved);
+ }
+ v.into_iter()
+}
+
#[derive(Debug)]
pub(crate) struct Autoderef<'a, 'db> {
pub(crate) table: &'a mut InferenceTable<'db>,
@@ -76,49 +110,43 @@ pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>,
ty: Ty,
) -> Option<(AutoderefKind, Ty)> {
- if let Some(derefed) = builtin_deref(&ty) {
+ if let Some(derefed) = builtin_deref(table, &ty, false) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
}
}
-// FIXME: replace uses of this with Autoderef above
-pub fn autoderef(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
- ty: Canonical<Ty>,
-) -> impl Iterator<Item = Canonical<Ty>> + '_ {
- let mut table = InferenceTable::new(db, env);
- let ty = table.instantiate_canonical(ty);
- let mut autoderef = Autoderef::new(&mut table, ty);
- let mut v = Vec::new();
- while let Some((ty, _steps)) = autoderef.next() {
- v.push(autoderef.table.canonicalize(ty).value);
- }
- v.into_iter()
-}
-
-pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
- let _p = profile::span("deref");
- autoderef_step(table, ty).map(|(_, ty)| ty)
-}
-
-fn builtin_deref(ty: &Ty) -> Option<&Ty> {
+pub(crate) fn builtin_deref<'ty>(
+ table: &mut InferenceTable<'_>,
+ ty: &'ty Ty,
+ explicit: bool,
+) -> Option<&'ty Ty> {
match ty.kind(Interner) {
- TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
+ TyKind::Ref(.., ty) => Some(ty),
+ // FIXME: Maybe accept this but diagnose if its not explicit?
+ TyKind::Raw(.., ty) if explicit => Some(ty),
+ &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
+ if crate::lang_items::is_box(table.db, adt) {
+ substs.at(Interner, 0).ty(Interner)
+ } else {
+ None
+ }
+ }
_ => None,
}
}
-fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
+pub(crate) fn deref_by_trait(
+ table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
+ ty: Ty,
+) -> Option<Ty> {
let _p = profile::span("deref_by_trait");
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
// don't try to deref unknown variables
return None;
}
- let db = table.db;
let deref_trait =
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index 03e944359..eec57ba3f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -18,7 +18,6 @@ use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
- ValueTyDefId,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -195,6 +194,19 @@ impl TyBuilder<()> {
params.placeholder_subst(db)
}
+ pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
+ let params = generics(db.upcast(), def.into());
+ Substitution::from_iter(
+ Interner,
+ params.iter_id().map(|id| match id {
+ either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
+ either::Either::Right(id) => {
+ unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
+ }
+ }),
+ )
+ }
+
pub fn subst_for_def(
db: &dyn HirDatabase,
def: impl Into<GenericDefId>,
@@ -233,6 +245,25 @@ impl TyBuilder<()> {
TyBuilder::new((), params, parent_subst)
}
+ pub fn subst_for_closure(
+ db: &dyn HirDatabase,
+ parent: DefWithBodyId,
+ sig_ty: Ty,
+ ) -> Substitution {
+ let sig_ty = sig_ty.cast(Interner);
+ let self_subst = iter::once(&sig_ty);
+ let Some(parent) = parent.as_generic_def_id() else {
+ return Substitution::from_iter(Interner, self_subst);
+ };
+ Substitution::from_iter(
+ Interner,
+ self_subst
+ .chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner))
+ .cloned()
+ .collect::<Vec<_>>(),
+ )
+ }
+
pub fn build(self) -> Substitution {
let ((), subst) = self.build_internal();
subst
@@ -362,21 +393,4 @@ impl TyBuilder<Binders<Ty>> {
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
-
- pub fn value_ty(
- db: &dyn HirDatabase,
- def: ValueTyDefId,
- parent_subst: Option<Substitution>,
- ) -> TyBuilder<Binders<Ty>> {
- let poly_value_ty = db.value_ty(def);
- let id = match def.to_generic_def_id() {
- Some(id) => id,
- None => {
- // static items
- assert!(parent_subst.is_none());
- return TyBuilder::new_empty(poly_value_ty);
- }
- };
- TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
- }
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index 28ae4c349..5dd8e2719 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -1,8 +1,8 @@
//! The implementation of `RustIrDatabase` for Chalk, which provides information
//! about the code that Chalk needs.
-use std::sync::Arc;
+use core::ops;
+use std::{iter, sync::Arc};
-use cov_mark::hit;
use tracing::debug;
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
@@ -10,9 +10,9 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
- expr::Movability,
+ hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget},
- AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
+ AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
};
use hir_expand::name::name;
@@ -25,7 +25,7 @@ use crate::{
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext,
- utils::generics,
+ utils::{generics, ClosureSubst},
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
@@ -108,17 +108,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
};
- fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
- let block = module.containing_block()?;
- hit!(block_local_impls);
- db.trait_impls_in_block(block)
- }
-
- // Note: Since we're using impls_for_trait, only impls where the trait
- // can be resolved should ever reach Chalk. impl_datum relies on that
- // and will panic if the trait can't be resolved.
- let in_deps = self.db.trait_impls_in_deps(self.krate);
- let in_self = self.db.trait_impls_in_crate(self.krate);
let trait_module = trait_.module(self.db.upcast());
let type_module = match self_ty_fp {
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
@@ -128,33 +117,62 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
_ => None,
};
- let impl_maps = [
- Some(in_deps),
- Some(in_self),
- local_impls(self.db, trait_module),
- type_module.and_then(|m| local_impls(self.db, m)),
- ];
- let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
+ let mut def_blocks =
+ [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
- let result: Vec<_> = if fps.is_empty() {
- debug!("Unrestricted search for {:?} impls...", trait_);
- impl_maps
- .iter()
- .filter_map(|o| o.as_ref())
- .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
- .collect()
- } else {
- impl_maps
- .iter()
- .filter_map(|o| o.as_ref())
- .flat_map(|impls| {
- fps.iter().flat_map(move |fp| {
- impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
- })
- })
- .collect()
- };
+ // Note: Since we're using impls_for_trait, only impls where the trait
+ // can be resolved should ever reach Chalk. impl_datum relies on that
+ // and will panic if the trait can't be resolved.
+ let in_deps = self.db.trait_impls_in_deps(self.krate);
+ let in_self = self.db.trait_impls_in_crate(self.krate);
+
+ let block_impls = iter::successors(self.block, |&block_id| {
+ cov_mark::hit!(block_local_impls);
+ self.db.block_def_map(block_id).parent().and_then(|module| module.containing_block())
+ })
+ .inspect(|&block_id| {
+ // make sure we don't search the same block twice
+ def_blocks.iter_mut().for_each(|block| {
+ if *block == Some(block_id) {
+ *block = None;
+ }
+ });
+ })
+ .map(|block_id| self.db.trait_impls_in_block(block_id));
+
+ let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
+ let mut result = vec![];
+ match fps {
+ [] => {
+ debug!("Unrestricted search for {:?} impls...", trait_);
+ let mut f = |impls: &TraitImpls| {
+ result.extend(impls.for_trait(trait_).map(id_to_chalk));
+ };
+ f(&in_self);
+ in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
+ block_impls.for_each(|it| f(&it));
+ def_blocks
+ .into_iter()
+ .flatten()
+ .for_each(|it| f(&self.db.trait_impls_in_block(it)));
+ }
+ fps => {
+ let mut f =
+ |impls: &TraitImpls| {
+ result.extend(fps.iter().flat_map(|fp| {
+ impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
+ }));
+ };
+ f(&in_self);
+ in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
+ block_impls.for_each(|it| f(&it));
+ def_blocks
+ .into_iter()
+ .flatten()
+ .for_each(|it| f(&self.db.trait_impls_in_block(it)));
+ }
+ }
debug!("impls_for_trait returned {} impls", result.len());
result
@@ -193,7 +211,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
&self,
environment: &chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner> {
- self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
+ self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone())
}
fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
@@ -321,7 +339,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_closure_id: chalk_ir::ClosureId<Interner>,
substs: &chalk_ir::Substitution<Interner>,
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
- let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
+ let sig_ty = ClosureSubst(substs).sig_ty();
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
let io = rust_ir::FnDefInputsAndOutputDatum {
argument_types: sig.params().to_vec(),
@@ -347,13 +365,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
let id = from_chalk_trait_id(trait_id);
- self.db.trait_data(id).name.to_string()
+ self.db.trait_data(id).name.display(self.db.upcast()).to_string()
}
fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
match adt_id {
- hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(),
- hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(),
- hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(),
+ hir_def::AdtId::StructId(id) => {
+ self.db.struct_data(id).name.display(self.db.upcast()).to_string()
+ }
+ hir_def::AdtId::EnumId(id) => {
+ self.db.enum_data(id).name.display(self.db.upcast()).to_string()
+ }
+ hir_def::AdtId::UnionId(id) => {
+ self.db.union_data(id).name.display(self.db.upcast()).to_string()
+ }
}
}
fn adt_size_align(&self, _id: chalk_ir::AdtId<Interner>) -> Arc<rust_ir::AdtSizeAlign> {
@@ -362,7 +386,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
let id = self.db.associated_ty_data(assoc_ty_id).name;
- self.db.type_alias_data(id).name.to_string()
+ self.db.type_alias_data(id).name.display(self.db.upcast()).to_string()
}
fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
format!("Opaque_{}", opaque_ty_id.0)
@@ -373,7 +397,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_datum(
&self,
id: chalk_ir::GeneratorId<Interner>,
- ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
+ ) -> Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
let (parent, expr) = self.db.lookup_intern_generator(id.into());
// We fill substitution with unknown type, because we only need to know whether the generic
@@ -398,8 +422,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
let input_output = crate::make_type_and_const_binders(it, input_output);
let movability = match self.db.body(parent)[expr] {
- hir_def::expr::Expr::Closure {
- closure_kind: hir_def::expr::ClosureKind::Generator(movability),
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::Generator(movability),
..
} => movability,
_ => unreachable!("non generator expression interned as generator"),
@@ -414,7 +438,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_witness_datum(
&self,
id: chalk_ir::GeneratorId<Interner>,
- ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
+ ) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
// FIXME: calculate inner types
let inner_types =
rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) };
@@ -435,7 +459,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
}
-impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
+impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
fn fn_def_variance(
&self,
fn_def_id: chalk_ir::FnDefId<Interner>,
@@ -451,9 +475,10 @@ impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
pub(crate) fn program_clauses_for_chalk_env_query(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
environment: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner> {
- chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment)
+ chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment)
}
pub(crate) fn associated_ty_data_query(
@@ -472,7 +497,7 @@ pub(crate) fn associated_ty_data_query(
let generic_params = generics(db.upcast(), type_alias.into());
// let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
- let ctx = crate::TyLoweringContext::new(db, &resolver)
+ let ctx = crate::TyLoweringContext::new(db, &resolver, type_alias.into())
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
@@ -567,6 +592,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
LangItem::Unpin => WellKnownTrait::Unpin,
LangItem::Unsize => WellKnownTrait::Unsize,
LangItem::Tuple => WellKnownTrait::Tuple,
+ LangItem::PointeeTrait => WellKnownTrait::Pointee,
_ => return None,
})
}
@@ -587,6 +613,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
WellKnownTrait::Tuple => LangItem::Tuple,
WellKnownTrait::Unpin => LangItem::Unpin,
WellKnownTrait::Unsize => LangItem::Unsize,
+ WellKnownTrait::Pointee => LangItem::PointeeTrait,
}
}
@@ -786,17 +813,17 @@ pub(crate) fn adt_variance_query(
)
}
+/// Returns instantiated predicates.
pub(super) fn convert_where_clauses(
db: &dyn HirDatabase,
def: GenericDefId,
substs: &Substitution,
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
- let generic_predicates = db.generic_predicates(def);
- let mut result = Vec::with_capacity(generic_predicates.len());
- for pred in generic_predicates.iter() {
- result.push(pred.clone().substitute(Interner, substs));
- }
- result
+ db.generic_predicates(def)
+ .iter()
+ .cloned()
+ .map(|pred| pred.substitute(Interner, substs))
+ .collect()
}
pub(super) fn generic_predicate_to_inline_bound(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 214189492..a8071591a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -1,24 +1,28 @@
//! Various extensions traits for Chalk types.
-use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
+use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
use hir_def::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
generics::TypeOrConstParamData,
lang_item::LangItem,
type_ref::Rawness,
- FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
+ DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
};
use crate::{
- db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
- from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
- CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
+ db::HirDatabase,
+ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
+ to_chalk_trait_id,
+ utils::{generics, ClosureSubst},
+ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
+ ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
};
pub trait TyExt {
fn is_unit(&self) -> bool;
fn is_integral(&self) -> bool;
+ fn is_scalar(&self) -> bool;
fn is_floating_point(&self) -> bool;
fn is_never(&self) -> bool;
fn is_unknown(&self) -> bool;
@@ -28,8 +32,10 @@ pub trait TyExt {
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>;
fn as_tuple(&self) -> Option<&Substitution>;
+ fn as_closure(&self) -> Option<ClosureId>;
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
+ fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>;
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
@@ -44,6 +50,7 @@ pub trait TyExt {
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
+ fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
/// FIXME: Get rid of this, it's not a good abstraction
fn equals_ctor(&self, other: &Ty) -> bool;
@@ -62,6 +69,10 @@ impl TyExt for Ty {
)
}
+ fn is_scalar(&self) -> bool {
+ matches!(self.kind(Interner), TyKind::Scalar(_))
+ }
+
fn is_floating_point(&self) -> bool {
matches!(
self.kind(Interner),
@@ -128,12 +139,20 @@ impl TyExt for Ty {
}
}
+ fn as_closure(&self) -> Option<ClosureId> {
+ match self.kind(Interner) {
+ TyKind::Closure(id, _) => Some(*id),
+ _ => None,
+ }
+ }
+
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
match self.callable_def(db) {
Some(CallableDefId::FunctionId(func)) => Some(func),
Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
}
}
+
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
match self.kind(Interner) {
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
@@ -141,6 +160,13 @@ impl TyExt for Ty {
}
}
+ fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> {
+ match self.kind(Interner) {
+ TyKind::Raw(mutability, ty) => Some((ty, *mutability)),
+ _ => None,
+ }
+ }
+
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
match self.kind(Interner) {
TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
@@ -176,10 +202,7 @@ impl TyExt for Ty {
let sig = db.callable_item_signature(callable_def);
Some(sig.substitute(Interner, parameters))
}
- TyKind::Closure(.., substs) => {
- let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
- sig_param.callable_sig(db)
- }
+ TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
_ => None,
}
}
@@ -318,6 +341,20 @@ impl TyExt for Ty {
}
}
+ fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
+ let crate_id = owner.module(db.upcast()).krate();
+ let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
+ return false;
+ };
+ let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
+ let env = db.trait_environment_for_body(owner);
+ let goal = Canonical {
+ value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
+ binders: CanonicalVarKinds::empty(Interner),
+ };
+ db.trait_solve(crate_id, None, goal).is_some()
+ }
+
fn equals_ctor(&self, other: &Ty) -> bool {
match (self.kind(Interner), other.kind(Interner)) {
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 5830c4898..262341c6e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -3,19 +3,20 @@
use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{
- expr::Expr,
- path::ModPath,
+ hir::Expr,
+ path::Path,
resolver::{Resolver, ValueNs},
- type_ref::ConstRef,
- ConstId, EnumVariantId,
+ type_ref::LiteralConstRef,
+ ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
};
use la_arena::{Idx, RawIdx};
use stdx::never;
+use triomphe::Arc;
use crate::{
- db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
- to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
- Interner, MemoryMap, Ty, TyBuilder,
+ db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
+ mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
+ ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
};
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@@ -57,7 +58,7 @@ pub enum ConstEvalError {
impl From<MirLowerError> for ConstEvalError {
fn from(value: MirLowerError) -> Self {
match value {
- MirLowerError::ConstEvalError(e) => *e,
+ MirLowerError::ConstEvalError(_, e) => *e,
_ => ConstEvalError::MirLowerError(value),
}
}
@@ -72,10 +73,11 @@ impl From<MirEvalError> for ConstEvalError {
pub(crate) fn path_to_const(
db: &dyn HirDatabase,
resolver: &Resolver,
- path: &ModPath,
+ path: &Path,
mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
+ expected_ty: Ty,
) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => {
@@ -89,7 +91,7 @@ pub(crate) fn path_to_const(
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
None => {
never!(
- "Generic list doesn't contain this param: {:?}, {}, {:?}",
+ "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args,
path,
p
@@ -100,6 +102,10 @@ pub(crate) fn path_to_const(
};
Some(ConstData { ty, value }.intern(Interner))
}
+ Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
+ ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
+ expected_ty,
+ )),
_ => None,
}
}
@@ -123,22 +129,28 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
}
/// Interns a constant scalar with the given type
-pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+pub fn intern_const_ref(
+ db: &dyn HirDatabase,
+ value: &LiteralConstRef,
+ ty: Ty,
+ krate: CrateId,
+) -> Const {
+ let layout = db.layout_of_ty(ty.clone(), krate);
let bytes = match value {
- ConstRef::Int(i) => {
+ LiteralConstRef::Int(i) => {
// FIXME: We should handle failure of layout better.
- let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+ let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
- ConstRef::UInt(i) => {
- let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+ LiteralConstRef::UInt(i) => {
+ let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
- ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
- ConstRef::Char(c) => {
+ LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
+ LiteralConstRef::Char(c) => {
ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
}
- ConstRef::Unknown => ConstScalar::Unknown,
+ LiteralConstRef::Unknown => ConstScalar::Unknown,
};
intern_const_scalar(bytes, ty)
}
@@ -147,19 +159,23 @@ pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: C
pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
intern_const_ref(
db,
- &value.map_or(ConstRef::Unknown, ConstRef::UInt),
+ &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
TyBuilder::usize(),
krate,
)
}
-pub fn try_const_usize(c: &Const) -> Option<u128> {
+pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
+ ConstScalar::UnevaluatedConst(c, subst) => {
+ let ec = db.const_eval(*c, subst.clone()).ok()?;
+ try_const_usize(db, &ec)
+ }
_ => None,
},
}
@@ -168,7 +184,16 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
pub(crate) fn const_eval_recover(
_: &dyn HirDatabase,
_: &[String],
- _: &ConstId,
+ _: &GeneralConstId,
+ _: &Substitution,
+) -> Result<Const, ConstEvalError> {
+ Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
+}
+
+pub(crate) fn const_eval_static_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &StaticId,
) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
@@ -183,11 +208,40 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
- const_id: ConstId,
+ def: GeneralConstId,
+ subst: Substitution,
) -> Result<Const, ConstEvalError> {
- let def = const_id.into();
- let body = db.mir_body(def)?;
- let c = interpret_mir(db, &body, false)?;
+ let body = match def {
+ GeneralConstId::ConstId(c) => {
+ db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
+ }
+ GeneralConstId::ConstBlockId(c) => {
+ let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c);
+ let body = db.body(parent);
+ let infer = db.infer(parent);
+ Arc::new(monomorphize_mir_body_bad(
+ db,
+ lower_to_mir(db, parent, &body, &infer, root)?,
+ subst,
+ db.trait_environment_for_body(parent),
+ )?)
+ }
+ GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
+ };
+ let c = interpret_mir(db, &body, false).0?;
+ Ok(c)
+}
+
+pub(crate) fn const_eval_static_query(
+ db: &dyn HirDatabase,
+ def: StaticId,
+) -> Result<Const, ConstEvalError> {
+ let body = db.monomorphized_mir_body(
+ def.into(),
+ Substitution::empty(Interner),
+ db.trait_environment_for_body(def.into()),
+ )?;
+ let c = interpret_mir(db, &body, false).0?;
Ok(c)
}
@@ -209,9 +263,13 @@ pub(crate) fn const_eval_discriminant_variant(
};
return Ok(value);
}
- let mir_body = db.mir_body(def)?;
- let c = interpret_mir(db, &mir_body, false)?;
- let c = try_const_usize(&c).unwrap() as i128;
+ let mir_body = db.monomorphized_mir_body(
+ def,
+ Substitution::empty(Interner),
+ db.trait_environment_for_body(def),
+ )?;
+ let c = interpret_mir(db, &mir_body, false).0?;
+ let c = try_const_usize(db, &c).unwrap() as i128;
Ok(c)
}
@@ -226,15 +284,16 @@ pub(crate) fn eval_to_const(
debruijn: DebruijnIndex,
) -> Const {
let db = ctx.db;
+ let infer = ctx.clone().resolve_all();
if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver;
- if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
+ if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
return c;
}
}
let infer = ctx.clone().resolve_all();
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
- if let Ok(result) = interpret_mir(db, &mir_body, true) {
+ if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
return result;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 6a29e8ce5..0db1fefbf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -1,8 +1,10 @@
-use base_db::fixture::WithFixture;
+use base_db::{fixture::WithFixture, FileId};
+use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
use crate::{
- consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
+ consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
+ Interner,
};
use super::{
@@ -10,9 +12,11 @@ use super::{
ConstEvalError,
};
+mod intrinsics;
+
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
- ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
+ ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
simplify(ConstEvalError::MirEvalError(*e))
}
_ => e,
@@ -20,17 +24,35 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
}
#[track_caller]
-fn check_fail(ra_fixture: &str, error: ConstEvalError) {
- assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
+fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ match eval_goal(&db, file_id) {
+ Ok(_) => panic!("Expected fail, but it succeeded"),
+ Err(e) => {
+ assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db))
+ }
+ }
}
#[track_caller]
fn check_number(ra_fixture: &str, answer: i128) {
- let r = eval_goal(ra_fixture).unwrap();
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ let r = match eval_goal(&db, file_id) {
+ Ok(t) => t,
+ Err(e) => {
+ let err = pretty_print_err(e, db);
+ panic!("Error in evaluating goal: {}", err);
+ }
+ };
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, _) => {
- assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
+ assert_eq!(
+ b,
+ &answer.to_le_bytes()[0..b.len()],
+ "Bytes differ. In decimal form: actual = {}, expected = {answer}",
+ i128::from_le_bytes(pad16(b, true))
+ );
}
x => panic!("Expected number but found {:?}", x),
},
@@ -38,16 +60,26 @@ fn check_number(ra_fixture: &str, answer: i128) {
}
}
-fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
- let (db, file_id) = TestDB::with_single_file(ra_fixture);
+fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
+ let mut err = String::new();
+ let span_formatter = |file, range| format!("{:?} {:?}", file, range);
+ match e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ }
+ .unwrap();
+ err
+}
+
+fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
let module_id = db.module_for_file(file_id);
- let def_map = module_id.def_map(&db);
+ let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
let const_id = scope
.declarations()
.find_map(|x| match x {
hir_def::ModuleDefId::ConstId(x) => {
- if db.const_data(x).name.as_ref()?.to_string() == "GOAL" {
+ if db.const_data(x).name.as_ref()?.display(db).to_string() == "GOAL" {
Some(x)
} else {
None
@@ -56,7 +88,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None,
})
.unwrap();
- db.const_eval(const_id)
+ db.const_eval(const_id.into(), Substitution::empty(Interner))
}
#[test]
@@ -72,8 +104,98 @@ fn bit_op() {
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
- // FIXME: report panic here
- check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
+ check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
+ check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
+ e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
+ });
+}
+
+#[test]
+fn floating_point() {
+ check_number(
+ r#"const GOAL: f64 = 2.0 + 3.0 * 5.5 - 8.;"#,
+ i128::from_le_bytes(pad16(&f64::to_le_bytes(10.5), true)),
+ );
+ check_number(
+ r#"const GOAL: f32 = 2.0 + 3.0 * 5.5 - 8.;"#,
+ i128::from_le_bytes(pad16(&f32::to_le_bytes(10.5), true)),
+ );
+ check_number(
+ r#"const GOAL: f32 = -90.0 + 36.0;"#,
+ i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)),
+ );
+}
+
+#[test]
+fn casts() {
+ check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12);
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: i32 = {
+ let a = [10, 20, 3, 15];
+ let x: &[i32] = &a;
+ let y: *const [i32] = x;
+ let z = y as *const i32;
+ unsafe { *z }
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: i16 = {
+ let a = &mut 5;
+ let z = a as *mut _;
+ unsafe { *z }
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = &[10, 20, 30, 40] as &[i32];
+ a.len()
+ };
+ "#,
+ 4,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = [10, 20, 3, 15];
+ let x: &[i32] = &a;
+ let y: *const [i32] = x;
+ let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
+ let q = z as *const str;
+ let p = q as *const [u8];
+ let w = unsafe { &*z };
+ w.len()
+ };
+ "#,
+ 4,
+ );
+ check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12);
+}
+
+#[test]
+fn raw_pointer_equality() {
+ check_number(
+ r#"
+ //- minicore: copy, eq
+ const GOAL: bool = {
+ let a = 2;
+ let p1 = a as *const i32;
+ let p2 = a as *const i32;
+ p1 == p2
+ };
+ "#,
+ 1,
+ );
}
#[test]
@@ -166,8 +288,7 @@ fn reference_autoderef() {
#[test]
fn overloaded_deref() {
- // FIXME: We should support this.
- check_fail(
+ check_number(
r#"
//- minicore: deref_mut
struct Foo;
@@ -185,9 +306,7 @@ fn overloaded_deref() {
*y + *x
};
"#,
- ConstEvalError::MirLowerError(MirLowerError::NotSupported(
- "explicit overloaded deref".into(),
- )),
+ 10,
);
}
@@ -219,6 +338,117 @@ fn overloaded_deref_autoref() {
}
#[test]
+fn overloaded_index() {
+ check_number(
+ r#"
+ //- minicore: index
+ struct Foo;
+
+ impl core::ops::Index<usize> for Foo {
+ type Output = i32;
+ fn index(&self, index: usize) -> &i32 {
+ if index == 7 {
+ &700
+ } else {
+ &1000
+ }
+ }
+ }
+
+ impl core::ops::IndexMut<usize> for Foo {
+ fn index_mut(&mut self, index: usize) -> &mut i32 {
+ if index == 7 {
+ &mut 7
+ } else {
+ &mut 10
+ }
+ }
+ }
+
+ const GOAL: i32 = {
+ (Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
+ };
+ "#,
+ 3417,
+ );
+}
+
+#[test]
+fn overloaded_binop() {
+ check_number(
+ r#"
+ //- minicore: add
+ enum Color {
+ Red,
+ Green,
+ Yellow,
+ }
+
+ use Color::*;
+
+ impl core::ops::Add for Color {
+ type Output = Color;
+ fn add(self, rhs: Color) -> Self::Output {
+ Yellow
+ }
+ }
+
+ impl core::ops::AddAssign for Color {
+ fn add_assign(&mut self, rhs: Color) {
+ *self = Red;
+ }
+ }
+
+ const GOAL: bool = {
+ let x = Red + Green;
+ let mut y = Green;
+ y += x;
+ x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow
+ };
+ "#,
+ 1,
+ );
+ check_number(
+ r#"
+ //- minicore: add
+ impl core::ops::Add for usize {
+ type Output = usize;
+ fn add(self, rhs: usize) -> Self::Output {
+ self + rhs
+ }
+ }
+
+ impl core::ops::AddAssign for usize {
+ fn add_assign(&mut self, rhs: usize) {
+ *self += rhs;
+ }
+ }
+
+ #[lang = "shl"]
+ pub trait Shl<Rhs = Self> {
+ type Output;
+
+ fn shl(self, rhs: Rhs) -> Self::Output;
+ }
+
+ impl Shl<u8> for usize {
+ type Output = usize;
+
+ fn shl(self, rhs: u8) -> Self::Output {
+ self << rhs
+ }
+ }
+
+ const GOAL: usize = {
+ let mut x = 10;
+ x += 20;
+ 2 + 2 + (x << 1u8)
+ };"#,
+ 64,
+ );
+}
+
+#[test]
fn function_call() {
check_number(
r#"
@@ -241,20 +471,6 @@ fn function_call() {
}
#[test]
-fn intrinsics() {
- check_number(
- r#"
- extern "rust-intrinsic" {
- pub fn size_of<T>() -> usize;
- }
-
- const GOAL: usize = size_of::<i32>();
- "#,
- 4,
- );
-}
-
-#[test]
fn trait_basic() {
check_number(
r#"
@@ -301,6 +517,35 @@ fn trait_method() {
}
#[test]
+fn trait_method_inside_block() {
+ check_number(
+ r#"
+trait Twait {
+ fn a(&self) -> i32;
+}
+
+fn outer() -> impl Twait {
+ struct Stwuct;
+
+ impl Twait for Stwuct {
+ fn a(&self) -> i32 {
+ 5
+ }
+ }
+ fn f() -> impl Twait {
+ let s = Stwuct;
+ s
+ }
+ f()
+}
+
+const GOAL: i32 = outer().a();
+ "#,
+ 5,
+ );
+}
+
+#[test]
fn generic_fn() {
check_number(
r#"
@@ -357,6 +602,16 @@ fn generic_fn() {
);
check_number(
r#"
+ const fn y<T>(b: T) -> (T, ) {
+ let alloc = b;
+ (alloc, )
+ }
+ const GOAL: u8 = y(2).0;
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
//- minicore: coerce_unsized, index, slice
fn bar<A, B>(a: A, b: B) -> B {
b
@@ -483,6 +738,66 @@ fn loops() {
"#,
4,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let mut x = 0;
+ loop {
+ x = x + 1;
+ if x == 5 {
+ break x + 2;
+ }
+ }
+ };
+ "#,
+ 7,
+ );
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ 'a: loop {
+ let x = 'b: loop {
+ let x = 'c: loop {
+ let x = 'd: loop {
+ let x = 'e: loop {
+ break 'd 1;
+ };
+ break 2 + x;
+ };
+ break 3 + x;
+ };
+ break 'a 4 + x;
+ };
+ break 5 + x;
+ }
+ };
+ "#,
+ 8,
+ );
+ check_number(
+ r#"
+ //- minicore: add
+ const GOAL: u8 = {
+ let mut x = 0;
+ 'a: loop {
+ 'b: loop {
+ 'c: while x < 20 {
+ 'd: while x < 5 {
+ 'e: loop {
+ x += 1;
+ continue 'c;
+ };
+ };
+ x += 1;
+ };
+ break 'a;
+ };
+ }
+ x
+ };
+ "#,
+ 20,
+ );
}
#[test]
@@ -523,6 +838,18 @@ fn for_loops() {
}
#[test]
+fn ranges() {
+ check_number(
+ r#"
+ //- minicore: range
+ const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end
+ + (10000..).start + (..100000).end + (..=1000000).end;
+ "#,
+ 1111111,
+ );
+}
+
+#[test]
fn recursion() {
check_number(
r#"
@@ -555,6 +882,38 @@ fn structs() {
"#,
17,
);
+ check_number(
+ r#"
+ struct Point {
+ x: i32,
+ y: i32,
+ }
+
+ const GOAL: i32 = {
+ let p = Point { x: 5, y: 2 };
+ let p2 = Point { x: 3, ..p };
+ p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y
+ };
+ "#,
+ 5232,
+ );
+ check_number(
+ r#"
+ struct Point {
+ x: i32,
+ y: i32,
+ }
+
+ const GOAL: i32 = {
+ let p = Point { x: 5, y: 2 };
+ let Point { x, y } = p;
+ let Point { x: x2, .. } = p;
+ let Point { y: y2, .. } = p;
+ x * 1000 + y * 100 + x2 * 10 + y2
+ };
+ "#,
+ 5252,
+ );
}
#[test]
@@ -599,13 +958,14 @@ fn tuples() {
);
check_number(
r#"
- struct TupleLike(i32, u8, i64, u16);
- const GOAL: u8 = {
+ struct TupleLike(i32, i64, u8, u16);
+ const GOAL: i64 = {
let a = TupleLike(10, 20, 3, 15);
- a.1
+ let TupleLike(b, .., c) = a;
+ a.1 * 100 + b as i64 + c as i64
};
"#,
- 20,
+ 2025,
);
check_number(
r#"
@@ -638,11 +998,17 @@ fn path_pattern_matching() {
use Season::*;
+ const MY_SEASON: Season = Summer;
+
+ impl Season {
+ const FALL: Season = Fall;
+ }
+
const fn f(x: Season) -> i32 {
match x {
Spring => 1,
- Summer => 2,
- Fall => 3,
+ MY_SEASON => 2,
+ Season::FALL => 3,
Winter => 4,
}
}
@@ -653,6 +1019,91 @@ fn path_pattern_matching() {
}
#[test]
+fn pattern_matching_literal() {
+ check_number(
+ r#"
+ const fn f(x: i32) -> i32 {
+ match x {
+ -1 => 1,
+ 1 => 10,
+ _ => 100,
+ }
+ }
+ const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
+ "#,
+ 211,
+ );
+ check_number(
+ r#"
+ const fn f(x: &str) -> i32 {
+ match x {
+ "f" => 1,
+ "foo" => 10,
+ "" => 100,
+ "bar" => 1000,
+ _ => 10000,
+ }
+ }
+ const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4;
+ "#,
+ 4321,
+ );
+}
+
+#[test]
+fn pattern_matching_range() {
+ check_number(
+ r#"
+ pub const L: i32 = 6;
+ mod x {
+ pub const R: i32 = 100;
+ }
+ const fn f(x: i32) -> i32 {
+ match x {
+ -1..=5 => x * 10,
+ L..=x::R => x * 100,
+ _ => x,
+ }
+ }
+ const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000);
+ "#,
+ 11008,
+ );
+}
+
+#[test]
+fn pattern_matching_slice() {
+ check_number(
+ r#"
+ //- minicore: slice, index, coerce_unsized, copy
+ const fn f(x: &[usize]) -> usize {
+ match x {
+ [a, b @ .., c, d] => *a + b.len() + *c + *d,
+ }
+ }
+ const GOAL: usize = f(&[10, 20, 3, 15, 1000, 60, 16]);
+ "#,
+ 10 + 4 + 60 + 16,
+ );
+ check_number(
+ r#"
+ //- minicore: slice, index, coerce_unsized, copy
+ const fn f(x: &[usize]) -> usize {
+ match x {
+ [] => 0,
+ [a] => *a,
+ &[a, b] => a + b,
+ [a, b @ .., c, d] => *a + b.len() + *c + *d,
+ }
+ }
+ const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100])
+ + f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]);
+ "#,
+ 33213,
+ );
+}
+
+#[test]
fn pattern_matching_ergonomics() {
check_number(
r#"
@@ -665,6 +1116,16 @@ fn pattern_matching_ergonomics() {
"#,
5,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let a = &(2, 3);
+ let &(x, y) = a;
+ x + y
+ };
+ "#,
+ 5,
+ );
}
#[test]
@@ -749,6 +1210,77 @@ fn function_param_patterns() {
}
#[test]
+fn match_guards() {
+ check_number(
+ r#"
+ //- minicore: option
+ fn f(x: Option<i32>) -> i32 {
+ match x {
+ y if let Some(42) = y => 42000,
+ Some(y) => y,
+ None => 10
+ }
+ }
+ const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None);
+ "#,
+ 42012,
+ );
+}
+
+#[test]
+fn result_layout_niche_optimization() {
+ check_number(
+ r#"
+ //- minicore: option, result
+ const GOAL: i32 = match Some(2).ok_or(Some(2)) {
+ Ok(x) => x,
+ Err(_) => 1000,
+ };
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
+ //- minicore: result
+ pub enum AlignmentEnum64 {
+ _Align1Shl0 = 1 << 0,
+ _Align1Shl1 = 1 << 1,
+ _Align1Shl2 = 1 << 2,
+ _Align1Shl3 = 1 << 3,
+ _Align1Shl4 = 1 << 4,
+ _Align1Shl5 = 1 << 5,
+ }
+ const GOAL: Result<AlignmentEnum64, ()> = {
+ let align = Err(());
+ align
+ };
+ "#,
+ 0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64`
+ );
+ check_number(
+ r#"
+ //- minicore: result
+ pub enum AlignmentEnum64 {
+ _Align1Shl0 = 1 << 0,
+ _Align1Shl1 = 1 << 1,
+ _Align1Shl2 = 1 << 2,
+ _Align1Shl3 = 1 << 3,
+ _Align1Shl4 = 1 << 4,
+ _Align1Shl5 = 1 << 5,
+ }
+ const GOAL: i32 = {
+ let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0);
+ match align {
+ Ok(_) => 2,
+ Err(_) => 1,
+ }
+ };
+ "#,
+ 2,
+ );
+}
+
+#[test]
fn options() {
check_number(
r#"
@@ -802,6 +1334,253 @@ fn options() {
}
#[test]
+fn from_trait() {
+ check_number(
+ r#"
+ //- minicore: from
+ struct E1(i32);
+ struct E2(i32);
+
+ impl From<E1> for E2 {
+ fn from(E1(x): E1) -> Self {
+ E2(1000 * x)
+ }
+ }
+ const GOAL: i32 = {
+ let x: E2 = E1(2).into();
+ x.0
+ };
+ "#,
+ 2000,
+ );
+}
+
+#[test]
+fn builtin_derive_macro() {
+ check_number(
+ r#"
+ //- minicore: clone, derive, builtin_impls
+ #[derive(Clone)]
+ enum Z {
+ Foo(Y),
+ Bar,
+ }
+ #[derive(Clone)]
+ struct X(i32, Z, i64)
+ #[derive(Clone)]
+ struct Y {
+ field1: i32,
+ field2: u8,
+ }
+
+ const GOAL: u8 = {
+ let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8);
+ let x = x.clone();
+ let Z::Foo(t) = x.1;
+ t.field2
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: default, derive, builtin_impls
+ #[derive(Default)]
+ struct X(i32, Y, i64)
+ #[derive(Default)]
+ struct Y {
+ field1: i32,
+ field2: u8,
+ }
+
+ const GOAL: u8 = {
+ let x = X::default();
+ x.1.field2
+ };
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn try_operator() {
+ check_number(
+ r#"
+ //- minicore: option, try
+ const fn f(x: Option<i32>, y: Option<i32>) -> Option<i32> {
+ Some(x? * y?)
+ }
+ const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
+ match f(x, y) {
+ Some(k) => k,
+ None => 5,
+ }
+ }
+ const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
+ "#,
+ 215,
+ );
+ check_number(
+ r#"
+ //- minicore: result, try, from
+ struct E1(i32);
+ struct E2(i32);
+
+ impl From<E1> for E2 {
+ fn from(E1(x): E1) -> Self {
+ E2(1000 * x)
+ }
+ }
+
+ const fn f(x: Result<i32, E1>) -> Result<i32, E2> {
+ Ok(x? * 10)
+ }
+ const fn g(x: Result<i32, E1>) -> i32 {
+ match f(x) {
+ Ok(k) => 7 * k,
+ Err(E2(k)) => 5 * k,
+ }
+ }
+ const GOAL: i32 = g(Ok(2)) + g(Err(E1(3)));
+ "#,
+ 15140,
+ );
+}
+
+#[test]
+fn try_block() {
+ check_number(
+ r#"
+ //- minicore: option, try
+ const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
+ let r = try { x? * y? };
+ match r {
+ Some(k) => k,
+ None => 5,
+ }
+ }
+ const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
+ "#,
+ 215,
+ );
+}
+
+#[test]
+fn closures() {
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let y = 5;
+ let c = |x| x + y;
+ c(2)
+ };
+ "#,
+ 7,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let y = 5;
+ let c = |(a, b): &(i32, i32)| *a + *b + y;
+ c(&(2, 3))
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let mut y = 5;
+ let c = |x| {
+ y = y + x;
+ };
+ c(2);
+ c(3);
+ y
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let c: fn(i32) -> i32 = |x| 2 * x;
+ c(2) + c(10)
+ };
+ "#,
+ 24,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ struct X(i32);
+ impl X {
+ fn mult(&mut self, n: i32) {
+ self.0 = self.0 * n
+ }
+ }
+ const GOAL: i32 = {
+ let x = X(1);
+ let c = || {
+ x.mult(2);
+ || {
+ x.mult(3);
+ || {
+ || {
+ x.mult(4);
+ || {
+ x.mult(x.0);
+ || {
+ x.0
+ }
+ }
+ }
+ }
+ }
+ };
+ let r = c()()()()()();
+ r + x.0
+ };
+ "#,
+ 24 * 24 * 2,
+ );
+}
+
+#[test]
+fn closure_and_impl_fn() {
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ fn closure_wrapper<F: FnOnce() -> i32>(c: F) -> impl FnOnce() -> F {
+ || c
+ }
+
+ const GOAL: i32 = {
+ let y = 5;
+ let c = closure_wrapper(|| y);
+ c()()
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ fn f<T, F: Fn() -> T>(t: F) -> impl Fn() -> T {
+ move || t()
+ }
+
+ const GOAL: i32 = f(|| 2)();
+ "#,
+ 2,
+ );
+}
+
+#[test]
fn or_pattern() {
check_number(
r#"
@@ -840,6 +1619,282 @@ fn or_pattern() {
}
#[test]
+fn function_pointer_in_constants() {
+ check_number(
+ r#"
+ struct Foo {
+ f: fn(u8) -> u8,
+ }
+ const FOO: Foo = Foo { f: add2 };
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = (FOO.f)(3);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn function_pointer() {
+ check_number(
+ r#"
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = {
+ let plus2 = add2;
+ plus2(3)
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = {
+ let plus2: fn(u8) -> u8 = add2;
+ plus2(3)
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn mult3(x: u8) -> u8 {
+ x * 3
+ }
+ const GOAL: u8 = {
+ let x = [add2, mult3];
+ x[0](1) + x[1](5)
+ };
+ "#,
+ 18,
+ );
+}
+
+#[test]
+fn enum_variant_as_function() {
+ check_number(
+ r#"
+ //- minicore: option
+ const GOAL: u8 = {
+ let f = Some;
+ f(3).unwrap_or(2)
+ };
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: option
+ const GOAL: u8 = {
+ let f: fn(u8) -> Option<u8> = Some;
+ f(3).unwrap_or(2)
+ };
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ enum Foo {
+ Add2(u8),
+ Mult3(u8),
+ }
+ use Foo::*;
+ const fn f(x: Foo) -> u8 {
+ match x {
+ Add2(x) => x + 2,
+ Mult3(x) => x * 3,
+ }
+ }
+ const GOAL: u8 = {
+ let x = [Add2, Mult3];
+ f(x[0](1)) + f(x[1](5))
+ };
+ "#,
+ 18,
+ );
+}
+
+#[test]
+fn function_traits() {
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
+ "#,
+ 15,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3);
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = {
+ let add2: fn(u8) -> u8 = add2;
+ call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
+ };
+ "#,
+ 15,
+ );
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(&&&&&add2, 3);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn dyn_trait() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ trait Foo {
+ fn foo(&self) -> u8 { 10 }
+ }
+ struct S1;
+ struct S2;
+ struct S3;
+ impl Foo for S1 {
+ fn foo(&self) -> u8 { 1 }
+ }
+ impl Foo for S2 {
+ fn foo(&self) -> u8 { 2 }
+ }
+ impl Foo for S3 {}
+ const GOAL: u8 = {
+ let x: &[&dyn Foo] = &[&S1, &S2, &S3];
+ x[0].foo() + x[1].foo() + x[2].foo()
+ };
+ "#,
+ 13,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ trait Foo {
+ fn foo(&self) -> i32 { 10 }
+ }
+ trait Bar {
+ fn bar(&self) -> i32 { 20 }
+ }
+
+ struct S;
+ impl Foo for S {
+ fn foo(&self) -> i32 { 200 }
+ }
+ impl Bar for dyn Foo {
+ fn bar(&self) -> i32 { 700 }
+ }
+ const GOAL: i32 = {
+ let x: &dyn Foo = &S;
+ x.bar() + x.foo()
+ };
+ "#,
+ 900,
+ );
+}
+
+#[test]
+fn boxes() {
+ check_number(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice
+use core::ops::{Deref, DerefMut};
+use core::{marker::Unsize, ops::CoerceUnsized};
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized> {
+ inner: *mut T,
+}
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ #[rustc_box]
+ Box::new(t)
+ }
+}
+
+impl<T: ?Sized> Deref for Box<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &**self
+ }
+}
+
+impl<T: ?Sized> DerefMut for Box<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut **self
+ }
+}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
+
+const GOAL: usize = {
+ let x = Box::new(5);
+ let y: Box<[i32]> = Box::new([1, 2, 3]);
+ *x + y.len()
+};
+"#,
+ 8,
+ );
+}
+
+#[test]
fn array_and_index() {
check_number(
r#"
@@ -867,9 +1922,42 @@ fn array_and_index() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = [1, 2, 3];
+ let x: &[i32] = &a;
+ let y = &*x;
+ y.len()
+ };"#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
5,
);
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: [u16; 5] = [1, 2, 3, 4, 5];"#,
+ 1 + (2 << 16) + (3 << 32) + (4 << 48) + (5 << 64),
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: [u16; 5] = [12; 5];"#,
+ 12 + (12 << 16) + (12 << 32) + (12 << 48) + (12 << 64),
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const LEN: usize = 4;
+ const GOAL: u16 = {
+ let x = [7; LEN];
+ x[2]
+ }"#,
+ 7,
+ );
}
#[test]
@@ -888,6 +1976,38 @@ fn byte_string() {
}
#[test]
+fn c_string() {
+ check_number(
+ r#"
+//- minicore: index, slice
+#[lang = "CStr"]
+pub struct CStr {
+ inner: [u8]
+}
+const GOAL: u8 = {
+ let a = c"hello";
+ a.inner[0]
+};
+ "#,
+ 104,
+ );
+ check_number(
+ r#"
+//- minicore: index, slice
+#[lang = "CStr"]
+pub struct CStr {
+ inner: [u8]
+}
+const GOAL: u8 = {
+ let a = c"hello";
+ a.inner[6]
+};
+ "#,
+ 0,
+ );
+}
+
+#[test]
fn consts() {
check_number(
r#"
@@ -901,6 +2021,48 @@ fn consts() {
}
#[test]
+fn statics() {
+ check_number(
+ r#"
+ //- minicore: cell
+ use core::cell::Cell;
+ fn f() -> i32 {
+ static S: Cell<i32> = Cell::new(10);
+ S.set(S.get() + 1);
+ S.get()
+ }
+ const GOAL: i32 = f() + f() + f();
+ "#,
+ 36,
+ );
+}
+
+#[test]
+fn extern_weak_statics() {
+ check_number(
+ r#"
+ extern "C" {
+ #[linkage = "extern_weak"]
+ static __dso_handle: *mut u8;
+ }
+ const GOAL: usize = __dso_handle as usize;
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn from_ne_bytes() {
+ check_number(
+ r#"
+//- minicore: int_impl
+const GOAL: u32 = u32::from_ne_bytes([44, 1, 0, 0]);
+ "#,
+ 300,
+ );
+}
+
+#[test]
fn enums() {
check_number(
r#"
@@ -927,14 +2089,14 @@ fn enums() {
"#,
0,
);
- let r = eval_goal(
+ let (db, file_id) = TestDB::with_single_file(
r#"
enum E { A = 1, B }
const GOAL: E = E::A;
"#,
- )
- .unwrap();
- assert_eq!(try_const_usize(&r), Some(1));
+ );
+ let r = eval_goal(&db, file_id).unwrap();
+ assert_eq!(try_const_usize(&db, &r), Some(1));
}
#[test]
@@ -946,7 +2108,7 @@ fn const_loop() {
const F2: i32 = 2 * F1;
const GOAL: i32 = F3;
"#,
- ConstEvalError::MirLowerError(MirLowerError::Loop),
+ |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
);
}
@@ -963,6 +2125,29 @@ fn const_transfer_memory() {
}
#[test]
+fn anonymous_const_block() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+
+ const fn f<T>() -> usize {
+ let r = const { size_of::<T>() };
+ r
+ }
+
+ const GOAL: usize = {
+ let x = const { 2 + const { 3 } };
+ let y = f::<i32>();
+ x + y
+ };
+ "#,
+ 9,
+ );
+}
+
+#[test]
fn const_impl_assoc() {
check_number(
r#"
@@ -970,9 +2155,9 @@ fn const_impl_assoc() {
impl U5 {
const VAL: usize = 5;
}
- const GOAL: usize = U5::VAL;
+ const GOAL: usize = U5::VAL + <U5>::VAL;
"#,
- 5,
+ 10,
);
}
@@ -987,12 +2172,61 @@ fn const_generic_subst_fn() {
"#,
11,
);
+ check_number(
+ r#"
+ fn f<const N: usize>(x: [i32; N]) -> usize {
+ N
+ }
+
+ trait ArrayExt {
+ fn f(self) -> usize;
+ }
+
+ impl<T, const N: usize> ArrayExt for [T; N] {
+ fn g(self) -> usize {
+ f(self)
+ }
+ }
+
+ const GOAL: usize = f([1, 2, 5]);
+ "#,
+ 3,
+ );
+}
+
+#[test]
+fn layout_of_type_with_associated_type_field_defined_inside_body() {
+ check_number(
+ r#"
+trait Tr {
+ type Ty;
+}
+
+struct St<T: Tr>(T::Ty);
+
+const GOAL: i64 = {
+ // if we move `St2` out of body, the test will fail, as we don't see the impl anymore. That
+ // case will probably be rejected by rustc in some later edition, but we should support this
+ // case.
+ struct St2;
+
+ impl Tr for St2 {
+ type Ty = i64;
+ }
+
+ struct Goal(St<St2>);
+
+ let x = Goal(St(5));
+ x.0.0
+};
+"#,
+ 5,
+ );
}
#[test]
fn const_generic_subst_assoc_const_impl() {
- // FIXME: this should evaluate to 5
- check_fail(
+ check_number(
r#"
struct Adder<const N: usize, const M: usize>;
impl<const N: usize, const M: usize> Adder<N, M> {
@@ -1000,14 +2234,42 @@ fn const_generic_subst_assoc_const_impl() {
}
const GOAL: usize = Adder::<2, 3>::VAL;
"#,
- ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
+ 5,
+ );
+}
+
+#[test]
+fn associated_types() {
+ check_number(
+ r#"
+ trait Tr {
+ type Item;
+ fn get_item(&self) -> Self::Item;
+ }
+
+ struct X(i32);
+ struct Y(i32);
+
+ impl Tr for X {
+ type Item = Y;
+ fn get_item(&self) -> Self::Item {
+ Y(self.0 + 2)
+ }
+ }
+
+ fn my_get_item<T: Tr>(x: T) -> <T as Tr>::Item {
+ x.get_item()
+ }
+
+ const GOAL: i32 = my_get_item(X(3)).0;
+ "#,
+ 5,
);
}
#[test]
fn const_trait_assoc() {
- // FIXME: this should evaluate to 0
- check_fail(
+ check_number(
r#"
struct U0;
trait ToConst {
@@ -1016,9 +2278,49 @@ fn const_trait_assoc() {
impl ToConst for U0 {
const VAL: usize = 0;
}
- const GOAL: usize = U0::VAL;
+ impl ToConst for i32 {
+ const VAL: usize = 32;
+ }
+ const GOAL: usize = U0::VAL + i32::VAL;
"#,
- ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
+ 32,
+ );
+ check_number(
+ r#"
+ struct S<T>(*mut T);
+
+ trait MySized: Sized {
+ const SIZE: S<Self> = S(1 as *mut Self);
+ }
+
+ impl MySized for i32 {
+ const SIZE: S<i32> = S(10 as *mut i32);
+ }
+
+ impl MySized for i64 {
+ }
+
+ const fn f<T: MySized>() -> usize {
+ T::SIZE.0 as usize
+ }
+
+ const GOAL: usize = f::<i32>() + f::<i64>() * 2;
+ "#,
+ 12,
+ );
+}
+
+#[test]
+fn panic_messages() {
+ check_fail(
+ r#"
+ //- minicore: panic
+ const GOAL: u8 = {
+ let x: u16 = 2;
+ panic!("hello");
+ };
+ "#,
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())),
);
}
@@ -1028,7 +2330,7 @@ fn exec_limits() {
r#"
const GOAL: usize = loop {};
"#,
- ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
);
check_fail(
r#"
@@ -1037,7 +2339,7 @@ fn exec_limits() {
}
const GOAL: i32 = f(0);
"#,
- ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
);
// Reasonable code should still work
check_number(
@@ -1062,7 +2364,7 @@ fn exec_limits() {
#[test]
fn type_error() {
- let e = eval_goal(
+ check_fail(
r#"
const GOAL: u8 = {
let x: u16 = 2;
@@ -1070,6 +2372,25 @@ fn type_error() {
y.0
};
"#,
+ |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
+ );
+}
+
+#[test]
+fn unsized_local() {
+ check_fail(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const fn x() -> SomeUnknownTypeThatDereferenceToSlice {
+ SomeUnknownTypeThatDereferenceToSlice
+ }
+
+ const GOAL: u16 = {
+ let y = x();
+ let z: &[u16] = &y;
+ z[1]
+ };
+ "#,
+ |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))),
);
- assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
new file mode 100644
index 000000000..e05d824db
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -0,0 +1,377 @@
+use super::*;
+
+#[test]
+fn size_of() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+
+ const GOAL: usize = size_of::<i32>();
+ "#,
+ 4,
+ );
+}
+
+#[test]
+fn transmute() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn transmute<T, U>(e: T) -> U;
+ }
+
+ const GOAL: i32 = transmute((1i16, 1i16));
+ "#,
+ 0x00010001,
+ );
+}
+
+#[test]
+fn const_eval_select() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
+ where
+ G: FnOnce<ARG, Output = RET>,
+ F: FnOnce<ARG, Output = RET>;
+ }
+
+ const fn in_const(x: i32, y: i32) -> i32 {
+ x + y
+ }
+
+ fn in_rt(x: i32, y: i32) -> i32 {
+ x + y
+ }
+
+ const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn wrapping_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn wrapping_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: u8 = wrapping_add(10, 250);
+ "#,
+ 4,
+ );
+}
+
+#[test]
+fn saturating_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn saturating_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: u8 = saturating_add(10, 250);
+ "#,
+ 255,
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn saturating_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: i8 = saturating_add(5, 8);
+ "#,
+ 13,
+ );
+}
+
+#[test]
+fn allocator() {
+ check_number(
+ r#"
+ extern "Rust" {
+ #[rustc_allocator]
+ fn __rust_alloc(size: usize, align: usize) -> *mut u8;
+ #[rustc_deallocator]
+ fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
+ #[rustc_reallocator]
+ fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
+ #[rustc_allocator_zeroed]
+ fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ptr = __rust_alloc(4, 1);
+ let ptr2 = ((ptr as usize) + 1) as *mut u8;
+ *ptr = 23;
+ *ptr2 = 32;
+ let ptr = __rust_realloc(ptr, 4, 1, 8);
+ let ptr2 = ((ptr as usize) + 1) as *mut u8;
+ *ptr + *ptr2
+ };
+ "#,
+ 55,
+ );
+}
+
+#[test]
+fn overflowing_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ }
+
+ const GOAL: u8 = add_with_overflow(1, 2).0;
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ }
+
+ const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn needs_drop() {
+ check_number(
+ r#"
+ //- minicore: copy, sized
+ extern "rust-intrinsic" {
+ pub fn needs_drop<T: ?Sized>() -> bool;
+ }
+ struct X;
+ const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
+ "#,
+ 1,
+ );
+}
+
+#[test]
+fn likely() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn likely(b: bool) -> bool;
+ pub fn unlikely(b: bool) -> bool;
+ }
+
+ const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false);
+ "#,
+ 1,
+ );
+}
+
+#[test]
+fn floating_point() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn sqrtf32(x: f32) -> f32;
+ pub fn powf32(a: f32, x: f32) -> f32;
+ pub fn fmaf32(a: f32, b: f32, c: f32) -> f32;
+ }
+
+ const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4);
+ "#,
+ i128::from_le_bytes(pad16(
+ &f32::to_le_bytes(1.2f32.sqrt() + 3.4f32.powf(5.6) + (-7.8f32).mul_add(1.3, 2.4)),
+ true,
+ )),
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn powif64(a: f64, x: i32) -> f64;
+ pub fn sinf64(x: f64) -> f64;
+ pub fn minnumf64(x: f64, y: f64) -> f64;
+ }
+
+ const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3);
+ "#,
+ i128::from_le_bytes(pad16(
+ &f64::to_le_bytes(1.2f64.powi(5) + 3.4f64.sin() + (-7.8f64).min(1.3)),
+ true,
+ )),
+ );
+}
+
+#[test]
+fn atomic() {
+ check_number(
+ r#"
+ //- minicore: copy
+ extern "rust-intrinsic" {
+ pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
+ pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
+ pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ }
+
+ fn should_not_reach() {
+ _ // fails the test if executed
+ }
+
+ const GOAL: i32 = {
+ let mut x = 5;
+ atomic_store_release(&mut x, 10);
+ let mut y = atomic_xchg_acquire(&mut x, 100);
+ atomic_xadd_acqrel(&mut y, 20);
+ if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) {
+ should_not_reach();
+ }
+ if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) {
+ should_not_reach();
+ }
+ if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) {
+ should_not_reach();
+ }
+ let mut z = atomic_xsub_seqcst(&mut x, -200);
+ atomic_xor_seqcst(&mut x, 1024);
+ atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2
+ };
+ "#,
+ 660 + 1024,
+ );
+}
+
+#[test]
+fn offset() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ar: &[(u8, u8, u8)] = &[
+ (10, 11, 12),
+ (20, 21, 22),
+ (30, 31, 32),
+ (40, 41, 42),
+ (50, 51, 52),
+ ];
+ let ar: *const [(u8, u8, u8)] = ar;
+ let ar = ar as *const (u8, u8, u8);
+ let element = *offset(ar, 2);
+ element.1
+ };
+ "#,
+ 31,
+ );
+}
+
+#[test]
+fn arith_offset() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ar: &[(u8, u8, u8)] = &[
+ (10, 11, 12),
+ (20, 21, 22),
+ (30, 31, 32),
+ (40, 41, 42),
+ (50, 51, 52),
+ ];
+ let ar: *const [(u8, u8, u8)] = ar;
+ let ar = ar as *const (u8, u8, u8);
+ let element = *arith_offset(arith_offset(ar, 102), -100);
+ element.1
+ };
+ "#,
+ 31,
+ );
+}
+
+#[test]
+fn copy_nonoverlapping() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ }
+
+ const GOAL: u8 = unsafe {
+ let mut x = 2;
+ let y = 5;
+ copy_nonoverlapping(&y, &mut x, 1);
+ x
+ };
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn copy() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
+ }
+
+ const GOAL: i32 = unsafe {
+ let mut x = [1i32, 2, 3, 4, 5];
+ let y = (&mut x as *mut _) as *mut i32;
+ let z = (y as usize + 4) as *const i32;
+ copy(z, y, 4);
+ x[0] + x[1] + x[2] + x[3] + x[4]
+ };
+ "#,
+ 19,
+ );
+}
+
+#[test]
+fn ctpop() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn ctpop<T: Copy>(x: T) -> T;
+ }
+
+ const GOAL: i64 = ctpop(-29);
+ "#,
+ 61,
+ );
+}
+
+#[test]
+fn cttz() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn cttz<T: Copy>(x: T) -> T;
+ }
+
+ const GOAL: i64 = cttz(-24);
+ "#,
+ 3,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 304c78767..9dd810f84 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -1,27 +1,27 @@
//! The home of `HirDatabase`, which is the Salsa database containing all the
//! type inference-related queries.
-use std::sync::Arc;
+use std::sync;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
- db::DefDatabase,
- expr::ExprId,
- layout::{Layout, LayoutError, TargetDataLayout},
- AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
- ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
+ DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
+ LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
use smallvec::SmallVec;
+use triomphe::Arc;
use crate::{
chalk_db,
consteval::ConstEvalError,
+ layout::{Layout, LayoutError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
- Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
- PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
- ValueTyDefId,
+ Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
+ Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
+ TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;
@@ -38,8 +38,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::mir::mir_body_recover)]
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
+ #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
+ fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
+
+ #[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
+ #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
+ fn monomorphized_mir_body(
+ &self,
+ def: DefWithBodyId,
+ subst: Substitution,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Result<Arc<MirBody>, MirLowerError>;
+
+ #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
+ fn monomorphized_mir_body_for_closure(
+ &self,
+ def: ClosureId,
+ subst: Substitution,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Result<Arc<MirBody>, MirLowerError>;
+
#[salsa::invoke(crate::mir::borrowck_query)]
- fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
+ fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
#[salsa::invoke(crate::lower::ty_query)]
#[salsa::cycle(crate::lower::ty_recover)]
@@ -57,7 +77,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)]
- fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
+ fn const_eval(&self, def: GeneralConstId, subst: Substitution)
+ -> Result<Const, ConstEvalError>;
+
+ #[salsa::invoke(crate::consteval::const_eval_static_query)]
+ #[salsa::cycle(crate::consteval::const_eval_static_recover)]
+ fn const_eval_static(&self, def: StaticId) -> Result<Const, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
@@ -71,7 +96,16 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::layout::layout_of_adt_query)]
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
- fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
+ fn layout_of_adt(
+ &self,
+ def: AdtId,
+ subst: Substitution,
+ krate: CrateId,
+ ) -> Result<Arc<Layout>, LayoutError>;
+
+ #[salsa::invoke(crate::layout::layout_of_ty_query)]
+ #[salsa::cycle(crate::layout::layout_of_ty_recover)]
+ fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result<Arc<Layout>, LayoutError>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
@@ -97,6 +131,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
+ #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
+ #[salsa::transparent]
+ fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>;
+
#[salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
@@ -108,7 +146,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
#[salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
- fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
+ fn inherent_impls_in_block(&self, block: BlockId) -> Arc<InherentImpls>;
/// Collects all crates in the dependency graph that have impls for the
/// given fingerprint. This is only used for primitive types and types
@@ -125,10 +163,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
- fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
+ fn trait_impls_in_block(&self, block: BlockId) -> Arc<TraitImpls>;
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
- fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
+ fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>;
// Interned IDs for Chalk integration
#[salsa::interned]
@@ -148,24 +186,34 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
#[salsa::invoke(chalk_db::associated_ty_data_query)]
- fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
+ fn associated_ty_data(
+ &self,
+ id: chalk_db::AssocTypeId,
+ ) -> sync::Arc<chalk_db::AssociatedTyDatum>;
#[salsa::invoke(chalk_db::trait_datum_query)]
- fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId)
- -> Arc<chalk_db::TraitDatum>;
+ fn trait_datum(
+ &self,
+ krate: CrateId,
+ trait_id: chalk_db::TraitId,
+ ) -> sync::Arc<chalk_db::TraitDatum>;
#[salsa::invoke(chalk_db::struct_datum_query)]
fn struct_datum(
&self,
krate: CrateId,
struct_id: chalk_db::AdtId,
- ) -> Arc<chalk_db::StructDatum>;
+ ) -> sync::Arc<chalk_db::StructDatum>;
#[salsa::invoke(chalk_db::impl_datum_query)]
- fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc<chalk_db::ImplDatum>;
+ fn impl_datum(
+ &self,
+ krate: CrateId,
+ impl_id: chalk_db::ImplId,
+ ) -> sync::Arc<chalk_db::ImplDatum>;
#[salsa::invoke(chalk_db::fn_def_datum_query)]
- fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
+ fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>;
#[salsa::invoke(chalk_db::fn_def_variance_query)]
fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
@@ -178,7 +226,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
&self,
krate: CrateId,
id: chalk_db::AssociatedTyValueId,
- ) -> Arc<chalk_db::AssociatedTyValue>;
+ ) -> sync::Arc<chalk_db::AssociatedTyValue>;
#[salsa::invoke(crate::traits::normalize_projection_query)]
#[salsa::transparent]
@@ -193,6 +241,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve(
&self,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
@@ -200,6 +249,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve_query(
&self,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
@@ -207,20 +257,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn program_clauses_for_chalk_env(
&self,
krate: CrateId,
+ block: Option<BlockId>,
env: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner>;
}
fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
let _p = profile::span("infer:wait").detail(|| match def {
- DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
- DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
- DefWithBodyId::ConstId(it) => {
- db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
+ DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::StaticId(it) => {
+ db.static_data(it).name.clone().display(db.upcast()).to_string()
}
+ DefWithBodyId::ConstId(it) => db
+ .const_data(it)
+ .name
+ .clone()
+ .unwrap_or_else(Name::missing)
+ .display(db.upcast())
+ .to_string(),
DefWithBodyId::VariantId(it) => {
- db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
}
+ DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
});
db.infer_query(def)
}
@@ -228,10 +286,11 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
fn trait_solve_wait(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution> {
let _p = profile::span("trait_solve::wait");
- db.trait_solve_query(krate, goal)
+ db.trait_solve_query(krate, block, goal)
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index d36b93e3b..1233469b9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -16,8 +16,8 @@ use std::fmt;
use base_db::CrateId;
use hir_def::{
- adt::VariantData,
- expr::{Pat, PatId},
+ data::adt::VariantData,
+ hir::{Pat, PatId},
src::HasSource,
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
StructId,
@@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> {
}
// Check the function name.
- let function_name = data.name.to_string();
+ let function_name = data.name.display(self.db.upcast()).to_string();
let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
current_name: data.name.clone(),
suggested_text: new_name,
@@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> {
id,
Replacement {
current_name: bind_name.clone(),
- suggested_text: to_lower_snake_case(&bind_name.to_string())?,
+ suggested_text: to_lower_snake_case(
+ &bind_name.display(self.db.upcast()).to_string(),
+ )?,
expected_case: CaseType::LowerSnakeCase,
},
))
@@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Function,
ident: AstPtr::new(&ast_ptr),
expected_case: fn_name_replacement.expected_case,
- ident_text: fn_name_replacement.current_name.to_string(),
+ ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: fn_name_replacement.suggested_text,
};
@@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> {
ident_type,
ident: AstPtr::new(&name_ast),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement
+ .current_name
+ .display(self.db.upcast())
+ .to_string(),
suggested_text: replacement.suggested_text,
};
@@ -362,7 +367,7 @@ impl<'a> DeclValidator<'a> {
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
// Check the structure name.
- let struct_name = data.name.to_string();
+ let struct_name = data.name.display(self.db.upcast()).to_string();
let struct_name_replacement = if !non_camel_case_allowed {
to_camel_case(&struct_name).map(|new_name| Replacement {
current_name: data.name.clone(),
@@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> {
if !non_snake_case_allowed {
if let VariantData::Record(fields) = data.variant_data.as_ref() {
for (_, field) in fields.iter() {
- let field_name = field.name.to_string();
+ let field_name = field.name.display(self.db.upcast()).to_string();
if let Some(new_name) = to_lower_snake_case(&field_name) {
let replacement = Replacement {
current_name: field.name.clone(),
@@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Structure,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Field,
ident: AstPtr::new(&ast_ptr),
expected_case: field_to_rename.expected_case,
- ident_text: field_to_rename.current_name.to_string(),
+ ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(),
suggested_text: field_to_rename.suggested_text,
};
@@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> {
}
// Check the enum name.
- let enum_name = data.name.to_string();
+ let enum_name = data.name.display(self.db.upcast()).to_string();
let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
current_name: data.name.clone(),
suggested_text: new_name,
@@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> {
.filter_map(|(_, variant)| {
Some(Replacement {
current_name: variant.name.clone(),
- suggested_text: to_camel_case(&variant.name.to_string())?,
+ suggested_text: to_camel_case(
+ &variant.name.display(self.db.upcast()).to_string(),
+ )?,
expected_case: CaseType::UpperCamelCase,
})
})
@@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Enum,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Variant,
ident: AstPtr::new(&ast_ptr),
expected_case: variant_to_rename.expected_case,
- ident_text: variant_to_rename.current_name.to_string(),
+ ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(),
suggested_text: variant_to_rename.suggested_text,
};
@@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> {
None => return,
};
- let const_name = name.to_string();
+ let const_name = name.display(self.db.upcast()).to_string();
let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) {
Replacement {
current_name: name.clone(),
@@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Constant,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> {
let name = &data.name;
- let static_name = name.to_string();
+ let static_name = name.display(self.db.upcast()).to_string();
let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) {
Replacement {
current_name: name.clone(),
@@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::StaticVariable,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 2e9066788..ab34dc88d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -3,7 +3,6 @@
//! fields, etc.
use std::fmt;
-use std::sync::Arc;
use either::Either;
use hir_def::lang_item::LangItem;
@@ -12,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
use hir_expand::name;
use itertools::Itertools;
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use typed_arena::Arena;
use crate::{
@@ -27,7 +27,7 @@ use crate::{
pub(crate) use hir_def::{
body::Body,
- expr::{Expr, ExprId, MatchArm, Pat, PatId},
+ hir::{Expr, ExprId, MatchArm, Pat, PatId},
LocalFieldId, VariantId,
};
@@ -207,7 +207,7 @@ impl ExprValidator {
let report = compute_match_usefulness(&cx, &m_arms, scrut_ty);
- // FIXME Report unreacheble arms
+ // FIXME Report unreachable arms
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
let witnesses = report.non_exhaustiveness_witnesses;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index 859a37804..f8cdeaa5e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -1,6 +1,6 @@
//! Validation of matches.
//!
-//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match
+//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match
//! checking algorithm.
//!
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.
@@ -12,7 +12,7 @@ pub(crate) mod usefulness;
use chalk_ir::Mutability;
use hir_def::{
- adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
+ body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
};
use hir_expand::name::Name;
use stdx::{always, never};
@@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
let variant = self.infer.variant_resolution_for_pat(pat);
let kind = match self.body[pat] {
- hir_def::expr::Pat::Wild => PatKind::Wild,
+ hir_def::hir::Pat::Wild => PatKind::Wild,
- hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr),
+ hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr),
- hir_def::expr::Pat::Path(ref path) => {
+ hir_def::hir::Pat::Path(ref path) => {
return self.lower_path(pat, path);
}
- hir_def::expr::Pat::Tuple { ref args, ellipsis } => {
+ hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
let arity = match *ty.kind(Interner) {
TyKind::Tuple(arity, _) => arity,
_ => {
@@ -146,13 +146,14 @@ impl<'a> PatCtxt<'a> {
PatKind::Leaf { subpatterns }
}
- hir_def::expr::Pat::Bind { id, subpat, .. } => {
- let bm = self.infer.pat_binding_modes[&pat];
+ hir_def::hir::Pat::Bind { id, subpat, .. } => {
+ let bm = self.infer.binding_modes[id];
+ ty = &self.infer[id];
let name = &self.body.bindings[id].name;
match (bm, ty.kind(Interner)) {
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
(BindingMode::Ref(_), _) => {
- never!("`ref {}` has wrong type {:?}", name, ty);
+ never!("`ref {}` has wrong type {:?}", name.display(self.db.upcast()), ty);
self.errors.push(PatternError::UnexpectedType);
return Pat { ty: ty.clone(), kind: PatKind::Wild.into() };
}
@@ -161,13 +162,13 @@ impl<'a> PatCtxt<'a> {
PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
}
- hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
+ hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
self.lower_variant_or_leaf(pat, ty, subpatterns)
}
- hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => {
+ hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
let variant_data = variant.unwrap().variant_data(self.db.upcast());
let subpatterns = args
.iter()
@@ -187,12 +188,12 @@ impl<'a> PatCtxt<'a> {
}
}
}
- hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => {
+ hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => {
self.errors.push(PatternError::UnresolvedVariant);
PatKind::Wild
}
- hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
+ hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
_ => {
self.errors.push(PatternError::Unimplemented);
@@ -279,8 +280,8 @@ impl<'a> PatCtxt<'a> {
}
}
- fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
- use hir_def::expr::{Expr, Literal::Bool};
+ fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
+ use hir_def::hir::{Expr, Literal::Bool};
match self.body[expr] {
Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },
@@ -297,7 +298,7 @@ impl HirDisplay for Pat {
match &*self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Binding { name, subpattern } => {
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
if let Some(subpattern) = subpattern {
write!(f, " @ ")?;
subpattern.hir_fmt(f)?;
@@ -318,10 +319,14 @@ impl HirDisplay for Pat {
match variant {
VariantId::EnumVariantId(v) => {
let data = f.db.enum_data(v.parent);
- write!(f, "{}", data.variants[v.local_id].name)?;
+ write!(f, "{}", data.variants[v.local_id].name.display(f.db.upcast()))?;
+ }
+ VariantId::StructId(s) => {
+ write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))?
+ }
+ VariantId::UnionId(u) => {
+ write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))?
}
- VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
- VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?,
};
let variant_data = variant.variant_data(f.db.upcast());
@@ -335,7 +340,11 @@ impl HirDisplay for Pat {
.map(|p| {
printed += 1;
WriteWith(move |f| {
- write!(f, "{}: ", rec_fields[p.field].name)?;
+ write!(
+ f,
+ "{}: ",
+ rec_fields[p.field].name.display(f.db.upcast())
+ )?;
p.pattern.hir_fmt(f)
})
});
@@ -379,7 +388,7 @@ impl HirDisplay for Pat {
}
PatKind::Deref { subpattern } => {
match self.ty.kind(Interner) {
- TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?,
+ TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?,
&TyKind::Ref(mutbl, ..) => {
write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })?
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
index d130827a7..a0f6b9368 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -82,7 +82,7 @@ fn expand_or_pat(pat: &Pat) -> Vec<&Pat> {
pats
}
-/// [Constructor] uses this in umimplemented variants.
+/// [Constructor] uses this in unimplemented variants.
/// It allows porting match expressions from upstream algorithm without losing semantics.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(super) enum Void {}
@@ -384,7 +384,7 @@ impl Constructor {
TyKind::Tuple(arity, ..) => arity,
TyKind::Ref(..) => 1,
TyKind::Adt(adt, ..) => {
- if is_box(adt.0, pcx.cx.db) {
+ if is_box(pcx.cx.db, adt.0) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
1
@@ -772,7 +772,7 @@ impl<'p> Fields<'p> {
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
let ty = field_ty[fid].clone().substitute(Interner, substs);
- let ty = normalize(cx.db, cx.body, ty);
+ let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty);
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
let is_uninhabited = cx.is_uninhabited(&ty);
@@ -800,7 +800,7 @@ impl<'p> Fields<'p> {
}
TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())),
&TyKind::Adt(AdtId(adt), ref substs) => {
- if is_box(adt, cx.db) {
+ if is_box(cx.db, adt) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
@@ -905,7 +905,7 @@ impl<'p> DeconstructedPat<'p> {
}
fields = Fields::from_iter(cx, wilds)
}
- TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => {
+ TyKind::Adt(adt, substs) if is_box(cx.db, adt.0) => {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
@@ -992,7 +992,7 @@ impl<'p> DeconstructedPat<'p> {
})
.collect(),
},
- TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => {
+ TyKind::Adt(adt, _) if is_box(cx.db, adt.0) => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
index b89b4f2bf..217454499 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
@@ -1,4 +1,4 @@
-//! Pattern untilities.
+//! Pattern utilities.
//!
//! Originates from `rustc_hir::pat_util`
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
index c4d709a97..d737b24ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
@@ -755,7 +755,7 @@ pub(crate) enum Reachability {
/// The arm is reachable. This additionally carries a set of or-pattern branches that have been
/// found to be unreachable despite the overall arm being reachable. Used only in the presence
/// of or-patterns, otherwise it stays empty.
- // FIXME: store ureachable subpattern IDs
+ // FIXME: store unreachable subpattern IDs
Reachable,
/// The arm is unreachable.
Unreachable,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d25c0ccf0..9f9a56ffa 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -3,7 +3,7 @@
use hir_def::{
body::Body,
- expr::{Expr, ExprId, UnaryOp},
+ hir::{Expr, ExprId, UnaryOp},
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId,
};
@@ -18,9 +18,10 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
- DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
- false
- }
+ DefWithBodyId::StaticId(_)
+ | DefWithBodyId::ConstId(_)
+ | DefWithBodyId::VariantId(_)
+ | DefWithBodyId::InTypeConstId(_) => false,
};
if is_unsafe {
return res;
@@ -73,7 +74,7 @@ fn walk_unsafe(
}
Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current);
- let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path());
+ let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index bd3eccfe4..c1df24d17 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -2,37 +2,44 @@
//! HIR back into source code, and just displaying them for debugging/testing
//! purposes.
-use std::fmt::{self, Debug};
+use std::{
+ fmt::{self, Debug},
+ mem::size_of,
+};
use base_db::CrateId;
use chalk_ir::{BoundVar, TyKind};
use hir_def::{
- adt::VariantData,
- body,
+ data::adt::VariantData,
db::DefDatabase,
find_path,
generics::{TypeOrConstParamData, TypeParamProvenance},
item_scope::ItemInNs,
lang_item::{LangItem, LangItemTarget},
+ nameres::DefMap,
path::{Path, PathKind},
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
visibility::Visibility,
- HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
+ EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId,
+ TraitId,
};
use hir_expand::{hygiene::Hygiene, name::Name};
use intern::{Internable, Interned};
use itertools::Itertools;
+use la_arena::ArenaMap;
use smallvec::SmallVec;
+use stdx::never;
use crate::{
+ consteval::try_const_usize,
db::HirDatabase,
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
- layout::layout_of_ty,
+ layout::Layout,
lt_from_placeholder_idx,
mapping::from_chalk,
mir::pad16,
primitive, to_assoc_type_id,
- utils::{self, generics},
+ utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
@@ -64,6 +71,7 @@ pub struct HirFormatter<'a> {
curr_size: usize,
pub(crate) max_size: Option<usize>,
omit_verbose_types: bool,
+ closure_style: ClosureStyle,
display_target: DisplayTarget,
}
@@ -87,6 +95,7 @@ pub trait HirDisplay {
max_size: Option<usize>,
omit_verbose_types: bool,
display_target: DisplayTarget,
+ closure_style: ClosureStyle,
) -> HirDisplayWrapper<'a, Self>
where
Self: Sized,
@@ -95,7 +104,14 @@ pub trait HirDisplay {
!matches!(display_target, DisplayTarget::SourceCode { .. }),
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
);
- HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
+ HirDisplayWrapper {
+ db,
+ t: self,
+ max_size,
+ omit_verbose_types,
+ display_target,
+ closure_style,
+ }
}
/// Returns a `Display`able type that is human-readable.
@@ -109,6 +125,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@@ -128,6 +145,7 @@ pub trait HirDisplay {
t: self,
max_size,
omit_verbose_types: true,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@@ -138,6 +156,7 @@ pub trait HirDisplay {
&'a self,
db: &'a dyn HirDatabase,
module_id: ModuleId,
+ allow_opaque: bool,
) -> Result<String, DisplaySourceCodeError> {
let mut result = String::new();
match self.hir_fmt(&mut HirFormatter {
@@ -147,7 +166,8 @@ pub trait HirDisplay {
curr_size: 0,
max_size: None,
omit_verbose_types: false,
- display_target: DisplayTarget::SourceCode { module_id },
+ closure_style: ClosureStyle::ImplFn,
+ display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -166,6 +186,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Test,
}
}
@@ -235,26 +256,34 @@ pub enum DisplayTarget {
Diagnostics,
/// Display types for inserting them in source files.
/// The generated code should compile, so paths need to be qualified.
- SourceCode { module_id: ModuleId },
+ SourceCode { module_id: ModuleId, allow_opaque: bool },
/// Only for test purpose to keep real types
Test,
}
impl DisplayTarget {
- fn is_source_code(&self) -> bool {
+ fn is_source_code(self) -> bool {
matches!(self, Self::SourceCode { .. })
}
- fn is_test(&self) -> bool {
+
+ fn is_test(self) -> bool {
matches!(self, Self::Test)
}
+
+ fn allows_opaque(self) -> bool {
+ match self {
+ Self::SourceCode { allow_opaque, .. } => allow_opaque,
+ _ => true,
+ }
+ }
}
#[derive(Debug)]
pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
- Closure,
Generator,
+ OpaqueType,
}
pub enum HirDisplayError {
@@ -274,9 +303,25 @@ pub struct HirDisplayWrapper<'a, T> {
t: &'a T,
max_size: Option<usize>,
omit_verbose_types: bool,
+ closure_style: ClosureStyle,
display_target: DisplayTarget,
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum ClosureStyle {
+ /// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the
+ /// closure implements. This is the default.
+ ImplFn,
+ /// `|i32, i32| -> i32`
+ RANotation,
+ /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
+ ClosureWithId,
+ /// `{closure#14825}<i32, ()>`, useful for internal usage.
+ ClosureWithSubst,
+ /// `…`, which is the `TYPE_HINT_TRUNCATION`
+ Hide,
+}
+
impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
self.t.hir_fmt(&mut HirFormatter {
@@ -287,8 +332,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
max_size: self.max_size,
omit_verbose_types: self.omit_verbose_types,
display_target: self.display_target,
+ closure_style: self.closure_style,
})
}
+
+ pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {
+ self.closure_style = c;
+ self
+ }
}
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
@@ -330,7 +381,13 @@ impl HirDisplay for ProjectionTy {
let trait_ref = self.trait_ref(f.db);
write!(f, "<")?;
fmt_trait_ref(f, &trait_ref, true)?;
- write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
+ write!(
+ f,
+ ">::{}",
+ f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id))
+ .name
+ .display(f.db.upcast())
+ )?;
let proj_params_count =
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
@@ -373,10 +430,16 @@ impl HirDisplay for Const {
let id = from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.type_or_consts[id.local_id];
- write!(f, "{}", param_data.name().unwrap())
+ write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
+ Ok(())
}
ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
+ ConstScalar::UnevaluatedConst(c, parameters) => {
+ write!(f, "{}", c.name(f.db.upcast()))?;
+ hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?;
+ Ok(())
+ }
ConstScalar::Unknown => f.write_char('_'),
},
}
@@ -411,8 +474,11 @@ fn render_const_scalar(
memory_map: &MemoryMap,
ty: &Ty,
) -> Result<(), HirDisplayError> {
+ // FIXME: We need to get krate from the final callers of the hir display
+ // infrastructure and have it here as a field on `f`.
+ let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
match ty.kind(Interner) {
- chalk_ir::TyKind::Scalar(s) => match s {
+ TyKind::Scalar(s) => match s {
Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
Scalar::Char => {
let x = u128::from_le_bytes(pad16(b, false)) as u32;
@@ -440,22 +506,90 @@ fn render_const_scalar(
}
},
},
- chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
- chalk_ir::TyKind::Str => {
+ TyKind::Ref(_, _, t) => match t.kind(Interner) {
+ TyKind::Str => {
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
- let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]);
- let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
+ let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");
write!(f, "{s:?}")
}
- _ => f.write_str("<ref-not-supported>"),
+ TyKind::Slice(ty) => {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size_one = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size_one * count) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&[")?;
+ let mut first = true;
+ for i in 0..count {
+ if first {
+ first = false;
+ } else {
+ f.write_str(", ")?;
+ }
+ let offset = size_one * i;
+ render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?;
+ }
+ f.write_str("]")
+ }
+ TyKind::Dyn(_) => {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Ok(t) = memory_map.vtable.ty(ty_id) else {
+ return f.write_str("<ty-missing-in-vtable-map>");
+ };
+ let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&")?;
+ render_const_scalar(f, bytes, memory_map, t)
+ }
+ TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.0 {
+ hir_def::AdtId::StructId(s) => {
+ let data = f.db.struct_data(s);
+ write!(f, "&{}", data.name.display(f.db.upcast()))?;
+ Ok(())
+ }
+ _ => {
+ return f.write_str("<unsized-enum-or-union>");
+ }
+ },
+ _ => {
+ let addr = usize::from_le_bytes(match b.try_into() {
+ Ok(b) => b,
+ Err(_) => {
+ never!(
+ "tried rendering ty {:?} in const ref with incorrect byte count {}",
+ t,
+ b.len()
+ );
+ return f.write_str("<layout-error>");
+ }
+ });
+ let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&")?;
+ render_const_scalar(f, bytes, memory_map, t)
+ }
},
- chalk_ir::TyKind::Tuple(_, subst) => {
- // FIXME: Remove this line. If the target data layout is independent
- // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need
- // to get krate. Otherwise, we need to get krate from the final callers of the hir display
- // infrastructure and have it here as a field on `f`.
- let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
- let Ok(layout) = layout_of_ty(f.db, ty, krate) else {
+ TyKind::Tuple(_, subst) => {
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
return f.write_str("<layout-error>");
};
f.write_str("(")?;
@@ -468,7 +602,7 @@ fn render_const_scalar(
}
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
let offset = layout.fields.offset(id).bytes_usize();
- let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
f.write_str("<layout-error>")?;
continue;
};
@@ -477,62 +611,144 @@ fn render_const_scalar(
}
f.write_str(")")
}
- chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
- hir_def::AdtId::StructId(s) => {
- let data = f.db.struct_data(s);
- let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else {
+ TyKind::Adt(adt, subst) => {
+ let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ match adt.0 {
+ hir_def::AdtId::StructId(s) => {
+ let data = f.db.struct_data(s);
+ write!(f, "{}", data.name.display(f.db.upcast()))?;
+ let field_types = f.db.field_types(s.into());
+ render_variant_after_name(
+ &data.variant_data,
+ f,
+ &field_types,
+ adt.0.module(f.db.upcast()).krate(),
+ &layout,
+ subst,
+ b,
+ memory_map,
+ )
+ }
+ hir_def::AdtId::UnionId(u) => {
+ write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
+ }
+ hir_def::AdtId::EnumId(e) => {
+ let Some((var_id, var_layout)) =
+ detect_variant_from_bytes(&layout, f.db, krate, b, e) else {
+ return f.write_str("<failed-to-detect-variant>");
+ };
+ let data = &f.db.enum_data(e).variants[var_id];
+ write!(f, "{}", data.name.display(f.db.upcast()))?;
+ let field_types =
+ f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into());
+ render_variant_after_name(
+ &data.variant_data,
+ f,
+ &field_types,
+ adt.0.module(f.db.upcast()).krate(),
+ &var_layout,
+ subst,
+ b,
+ memory_map,
+ )
+ }
+ }
+ }
+ TyKind::FnDef(..) => ty.hir_fmt(f),
+ TyKind::Function(_) | TyKind::Raw(_, _) => {
+ let x = u128::from_le_bytes(pad16(b, false));
+ write!(f, "{:#X} as ", x)?;
+ ty.hir_fmt(f)
+ }
+ TyKind::Array(ty, len) => {
+ let Some(len) = try_const_usize(f.db, len) else {
+ return f.write_str("<unknown-array-len>");
+ };
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size_one = layout.size.bytes_usize();
+ f.write_str("[")?;
+ let mut first = true;
+ for i in 0..len as usize {
+ if first {
+ first = false;
+ } else {
+ f.write_str(", ")?;
+ }
+ let offset = size_one * i;
+ render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?;
+ }
+ f.write_str("]")
+ }
+ TyKind::Never => f.write_str("!"),
+ TyKind::Closure(_, _) => f.write_str("<closure>"),
+ TyKind::Generator(_, _) => f.write_str("<generator>"),
+ TyKind::GeneratorWitness(_, _) => f.write_str("<generator-witness>"),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => f.write_str("<extern-type>"),
+ TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(_)
+ | TyKind::AssociatedType(_, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => f.write_str("<placeholder-or-unknown-type>"),
+ // The below arms are unreachable, since we handled them in ref case.
+ TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str("<unsized-value>"),
+ }
+}
+
+fn render_variant_after_name(
+ data: &VariantData,
+ f: &mut HirFormatter<'_>,
+ field_types: &ArenaMap<LocalFieldId, Binders<Ty>>,
+ krate: CrateId,
+ layout: &Layout,
+ subst: &Substitution,
+ b: &[u8],
+ memory_map: &MemoryMap,
+) -> Result<(), HirDisplayError> {
+ match data {
+ VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
+ let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();
+ let ty = field_types[id].clone().substitute(Interner, subst);
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
return f.write_str("<layout-error>");
};
- match data.variant_data.as_ref() {
- VariantData::Record(fields) | VariantData::Tuple(fields) => {
- let field_types = f.db.field_types(s.into());
- let krate = adt.0.module(f.db.upcast()).krate();
- let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
- let offset = layout
- .fields
- .offset(u32::from(id.into_raw()) as usize)
- .bytes_usize();
- let ty = field_types[id].clone().substitute(Interner, subst);
- let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
- return f.write_str("<layout-error>");
- };
- let size = layout.size.bytes_usize();
- render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
- };
- let mut it = fields.iter();
- if matches!(data.variant_data.as_ref(), VariantData::Record(_)) {
- write!(f, "{} {{", data.name)?;
- if let Some((id, data)) = it.next() {
- write!(f, " {}: ", data.name)?;
- render_field(f, id)?;
- }
- for (id, data) in it {
- write!(f, ", {}: ", data.name)?;
- render_field(f, id)?;
- }
- write!(f, " }}")?;
- } else {
- let mut it = it.map(|x| x.0);
- write!(f, "{}(", data.name)?;
- if let Some(id) = it.next() {
- render_field(f, id)?;
- }
- for id in it {
- write!(f, ", ")?;
- render_field(f, id)?;
- }
- write!(f, ")")?;
- }
- return Ok(());
- }
- VariantData::Unit => write!(f, "{}", data.name),
+ let size = layout.size.bytes_usize();
+ render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
+ };
+ let mut it = fields.iter();
+ if matches!(data, VariantData::Record(_)) {
+ write!(f, " {{")?;
+ if let Some((id, data)) = it.next() {
+ write!(f, " {}: ", data.name.display(f.db.upcast()))?;
+ render_field(f, id)?;
+ }
+ for (id, data) in it {
+ write!(f, ", {}: ", data.name.display(f.db.upcast()))?;
+ render_field(f, id)?;
}
+ write!(f, " }}")?;
+ } else {
+ let mut it = it.map(|x| x.0);
+ write!(f, "(")?;
+ if let Some(id) = it.next() {
+ render_field(f, id)?;
+ }
+ for id in it {
+ write!(f, ", ")?;
+ render_field(f, id)?;
+ }
+ write!(f, ")")?;
}
- hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name),
- hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
- },
- chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
- _ => f.write_str("<not-supported>"),
+ return Ok(());
+ }
+ VariantData::Unit => Ok(()),
}
}
@@ -689,11 +905,17 @@ impl HirDisplay for Ty {
let sig = db.callable_item_signature(def).substitute(Interner, parameters);
f.start_location_link(def.into());
match def {
- CallableDefId::FunctionId(ff) => write!(f, "fn {}", db.function_data(ff).name)?,
- CallableDefId::StructId(s) => write!(f, "{}", db.struct_data(s).name)?,
- CallableDefId::EnumVariantId(e) => {
- write!(f, "{}", db.enum_data(e.parent).variants[e.local_id].name)?
+ CallableDefId::FunctionId(ff) => {
+ write!(f, "fn {}", db.function_data(ff).name.display(f.db.upcast()))?
+ }
+ CallableDefId::StructId(s) => {
+ write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))?
}
+ CallableDefId::EnumVariantId(e) => write!(
+ f,
+ "{}",
+ db.enum_data(e.parent).variants[e.local_id].name.display(f.db.upcast())
+ )?,
};
f.end_location_link();
if parameters.len(Interner) > 0 {
@@ -733,16 +955,16 @@ impl HirDisplay for Ty {
hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(),
};
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
}
- DisplayTarget::SourceCode { module_id } => {
+ DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
if let Some(path) = find_path::find_path(
db.upcast(),
ItemInNs::Types((*def_id).into()),
module_id,
false,
) {
- write!(f, "{path}")?;
+ write!(f, "{}", path.display(f.db.upcast()))?;
} else {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::PathNotFound,
@@ -752,82 +974,9 @@ impl HirDisplay for Ty {
}
f.end_location_link();
- if parameters.len(Interner) > 0 {
- let parameters_to_write = if f.display_target.is_source_code()
- || f.omit_verbose_types()
- {
- match self
- .as_generic_def(db)
- .map(|generic_def_id| db.generic_defaults(generic_def_id))
- .filter(|defaults| !defaults.is_empty())
- {
- None => parameters.as_slice(Interner),
- Some(default_parameters) => {
- fn should_show(
- parameter: &GenericArg,
- default_parameters: &[Binders<GenericArg>],
- i: usize,
- parameters: &Substitution,
- ) -> bool {
- if parameter.ty(Interner).map(|x| x.kind(Interner))
- == Some(&TyKind::Error)
- {
- return true;
- }
- if let Some(ConstValue::Concrete(c)) = parameter
- .constant(Interner)
- .map(|x| &x.data(Interner).value)
- {
- if c.interned == ConstScalar::Unknown {
- return true;
- }
- }
- let default_parameter = match default_parameters.get(i) {
- Some(x) => x,
- None => return true,
- };
- let actual_default =
- default_parameter.clone().substitute(Interner, &parameters);
- parameter != &actual_default
- }
- let mut default_from = 0;
- for (i, parameter) in parameters.iter(Interner).enumerate() {
- if should_show(parameter, &default_parameters, i, parameters) {
- default_from = i + 1;
- }
- }
- &parameters.as_slice(Interner)[0..default_from]
- }
- }
- } else {
- parameters.as_slice(Interner)
- };
- if !parameters_to_write.is_empty() {
- write!(f, "<")?;
-
- if f.display_target.is_source_code() {
- let mut first = true;
- for generic_arg in parameters_to_write {
- if !first {
- write!(f, ", ")?;
- }
- first = false;
-
- if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
- == Some(&TyKind::Error)
- {
- write!(f, "_")?;
- } else {
- generic_arg.hir_fmt(f)?;
- }
- }
- } else {
- f.write_joined(parameters_to_write, ", ")?;
- }
+ let generic_def = self.as_generic_def(db);
- write!(f, ">")?;
- }
- }
+ hir_fmt_generics(f, parameters, generic_def)?;
}
TyKind::AssociatedType(assoc_type_id, parameters) => {
let type_alias = from_assoc_type_id(*assoc_type_id);
@@ -841,12 +990,12 @@ impl HirDisplay for Ty {
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
if f.display_target.is_test() {
f.start_location_link(trait_.into());
- write!(f, "{}", trait_data.name)?;
+ write!(f, "{}", trait_data.name.display(f.db.upcast()))?;
f.end_location_link();
write!(f, "::")?;
f.start_location_link(type_alias.into());
- write!(f, "{}", type_alias_data.name)?;
+ write!(f, "{}", type_alias_data.name.display(f.db.upcast()))?;
f.end_location_link();
// Note that the generic args for the associated type come before those for the
// trait (including the self type).
@@ -869,10 +1018,15 @@ impl HirDisplay for Ty {
let alias = from_foreign_def_id(*type_alias);
let type_alias = db.type_alias_data(alias);
f.start_location_link(alias.into());
- write!(f, "{}", type_alias.name)?;
+ write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
f.end_location_link();
}
TyKind::OpaqueType(opaque_ty_id, parameters) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -919,26 +1073,52 @@ impl HirDisplay for Ty {
}
}
}
- TyKind::Closure(.., substs) => {
+ TyKind::Closure(id, substs) => {
if f.display_target.is_source_code() {
- return Err(HirDisplayError::DisplaySourceCodeError(
- DisplaySourceCodeError::Closure,
- ));
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ } else if f.closure_style != ClosureStyle::ImplFn {
+ never!("Only `impl Fn` is valid for displaying closures in source code");
+ }
}
- let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
+ match f.closure_style {
+ ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
+ ClosureStyle::ClosureWithId => {
+ return write!(f, "{{closure#{:?}}}", id.0.as_u32())
+ }
+ ClosureStyle::ClosureWithSubst => {
+ write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
+ return hir_fmt_generics(f, substs, None);
+ }
+ _ => (),
+ }
+ let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
if let Some(sig) = sig {
+ let (def, _) = db.lookup_intern_closure((*id).into());
+ let infer = db.infer(def);
+ let (_, kind) = infer.closure_info(id);
+ match f.closure_style {
+ ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
+ ClosureStyle::RANotation => write!(f, "|")?,
+ _ => unreachable!(),
+ }
if sig.params().is_empty() {
- write!(f, "||")?;
} else if f.should_truncate() {
- write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
+ write!(f, "{TYPE_HINT_TRUNCATION}")?;
} else {
- write!(f, "|")?;
f.write_joined(sig.params(), ", ")?;
- write!(f, "|")?;
};
-
- write!(f, " -> ")?;
- sig.ret().hir_fmt(f)?;
+ match f.closure_style {
+ ClosureStyle::ImplFn => write!(f, ")")?,
+ ClosureStyle::RANotation => write!(f, "|")?,
+ _ => unreachable!(),
+ }
+ if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
+ write!(f, " -> ")?;
+ sig.ret().hir_fmt(f)?;
+ }
} else {
write!(f, "{{closure}}")?;
}
@@ -950,7 +1130,11 @@ impl HirDisplay for Ty {
match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
- write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))?
+ write!(
+ f,
+ "{}",
+ p.name.clone().unwrap_or_else(Name::missing).display(f.db.upcast())
+ )?
}
TypeParamProvenance::ArgumentImplTrait => {
let substs = generics.placeholder_subst(db);
@@ -979,7 +1163,7 @@ impl HirDisplay for Ty {
}
},
TypeOrConstParamData::ConstParamData(p) => {
- write!(f, "{}", p.name)?;
+ write!(f, "{}", p.name.display(f.db.upcast()))?;
}
}
}
@@ -1004,6 +1188,11 @@ impl HirDisplay for Ty {
}
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -1067,6 +1256,88 @@ impl HirDisplay for Ty {
}
}
+fn hir_fmt_generics(
+ f: &mut HirFormatter<'_>,
+ parameters: &Substitution,
+ generic_def: Option<hir_def::GenericDefId>,
+) -> Result<(), HirDisplayError> {
+ let db = f.db;
+ let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len());
+ if parameters.len(Interner) + lifetime_args_count > 0 {
+ let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
+ match generic_def
+ .map(|generic_def_id| db.generic_defaults(generic_def_id))
+ .filter(|defaults| !defaults.is_empty())
+ {
+ None => parameters.as_slice(Interner),
+ Some(default_parameters) => {
+ fn should_show(
+ parameter: &GenericArg,
+ default_parameters: &[Binders<GenericArg>],
+ i: usize,
+ parameters: &Substitution,
+ ) -> bool {
+ if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error)
+ {
+ return true;
+ }
+ if let Some(ConstValue::Concrete(c)) =
+ parameter.constant(Interner).map(|x| &x.data(Interner).value)
+ {
+ if c.interned == ConstScalar::Unknown {
+ return true;
+ }
+ }
+ let default_parameter = match default_parameters.get(i) {
+ Some(x) => x,
+ None => return true,
+ };
+ let actual_default =
+ default_parameter.clone().substitute(Interner, &parameters);
+ parameter != &actual_default
+ }
+ let mut default_from = 0;
+ for (i, parameter) in parameters.iter(Interner).enumerate() {
+ if should_show(parameter, &default_parameters, i, parameters) {
+ default_from = i + 1;
+ }
+ }
+ &parameters.as_slice(Interner)[0..default_from]
+ }
+ }
+ } else {
+ parameters.as_slice(Interner)
+ };
+ if !parameters_to_write.is_empty() || lifetime_args_count != 0 {
+ write!(f, "<")?;
+ let mut first = true;
+ for _ in 0..lifetime_args_count {
+ if !first {
+ write!(f, ", ")?;
+ }
+ first = false;
+ write!(f, "'_")?;
+ }
+ for generic_arg in parameters_to_write {
+ if !first {
+ write!(f, ", ")?;
+ }
+ first = false;
+ if f.display_target.is_source_code()
+ && generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error)
+ {
+ write!(f, "_")?;
+ } else {
+ generic_arg.hir_fmt(f)?;
+ }
+ }
+
+ write!(f, ">")?;
+ }
+ }
+ Ok(())
+}
+
impl HirDisplay for CallableSig {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write!(f, "fn(")?;
@@ -1170,7 +1441,7 @@ fn write_bounds_like_dyn_trait(
// existential) here, which is the only thing that's
// possible in actual Rust, and hence don't print it
f.start_location_link(trait_.into());
- write!(f, "{}", f.db.trait_data(trait_).name)?;
+ write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link();
if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) {
if is_fn_trait {
@@ -1209,7 +1480,7 @@ fn write_bounds_like_dyn_trait(
let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
let type_alias = f.db.type_alias_data(assoc_ty_id);
f.start_location_link(assoc_ty_id.into());
- write!(f, "{}", type_alias.name)?;
+ write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
f.end_location_link();
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
@@ -1276,7 +1547,7 @@ fn fmt_trait_ref(
}
let trait_ = tr.hir_trait_id();
f.start_location_link(trait_.into());
- write!(f, "{}", f.db.trait_data(trait_).name)?;
+ write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link();
if tr.substitution.len(Interner) > 1 {
write!(f, "<")?;
@@ -1306,7 +1577,7 @@ impl HirDisplay for WhereClause {
write!(f, ">::",)?;
let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
f.start_location_link(type_alias.into());
- write!(f, "{}", f.db.type_alias_data(type_alias).name,)?;
+ write!(f, "{}", f.db.type_alias_data(type_alias).name.display(f.db.upcast()),)?;
f.end_location_link();
write!(f, " = ")?;
ty.hir_fmt(f)?;
@@ -1344,7 +1615,8 @@ impl HirDisplay for LifetimeData {
let id = lt_from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.lifetimes[id.local_id];
- write!(f, "{}", param_data.name)
+ write!(f, "{}", param_data.name.display(f.db.upcast()))?;
+ Ok(())
}
LifetimeData::Static => write!(f, "'static"),
LifetimeData::Erased => Ok(()),
@@ -1376,7 +1648,7 @@ pub fn write_visibility(
Visibility::Public => write!(f, "pub "),
Visibility::Module(vis_id) => {
let def_map = module_id.def_map(f.db.upcast());
- let root_module_id = def_map.module_id(def_map.root());
+ let root_module_id = def_map.module_id(DefMap::ROOT);
if vis_id == module_id {
// pub(self) or omitted
Ok(())
@@ -1420,7 +1692,7 @@ impl HirDisplay for TypeRef {
};
write!(f, "&")?;
if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name)?;
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
}
write!(f, "{mutability}")?;
inner.hir_fmt(f)?;
@@ -1428,7 +1700,7 @@ impl HirDisplay for TypeRef {
TypeRef::Array(inner, len) => {
write!(f, "[")?;
inner.hir_fmt(f)?;
- write!(f, "; {len}]")?;
+ write!(f, "; {}]", len.display(f.db.upcast()))?;
}
TypeRef::Slice(inner) => {
write!(f, "[")?;
@@ -1445,7 +1717,7 @@ impl HirDisplay for TypeRef {
for index in 0..function_parameters.len() {
let (param_name, param_type) = &function_parameters[index];
if let Some(name) = param_name {
- write!(f, "{name}: ")?;
+ write!(f, "{}: ", name.display(f.db.upcast()))?;
}
param_type.hir_fmt(f)?;
@@ -1477,7 +1749,10 @@ impl HirDisplay for TypeRef {
}
TypeRef::Macro(macro_call) => {
let macro_call = macro_call.to_node(f.db.upcast());
- let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic());
+ let ctx = hir_def::lower::LowerCtx::with_hygiene(
+ f.db.upcast(),
+ &Hygiene::new_unhygienic(),
+ );
match macro_call.path() {
Some(path) => match Path::from_src(path, &ctx) {
Some(path) => path.hir_fmt(f)?,
@@ -1503,9 +1778,13 @@ impl HirDisplay for TypeBound {
}
path.hir_fmt(f)
}
- TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
+ TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name.display(f.db.upcast())),
TypeBound::ForLifetime(lifetimes, path) => {
- write!(f, "for<{}> ", lifetimes.iter().format(", "))?;
+ write!(
+ f,
+ "for<{}> ",
+ lifetimes.iter().map(|it| it.display(f.db.upcast())).format(", ")
+ )?;
path.hir_fmt(f)
}
TypeBound::Error => write!(f, "{{error}}"),
@@ -1551,7 +1830,7 @@ impl HirDisplay for Path {
if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
write!(f, "::")?;
}
- write!(f, "{}", segment.name)?;
+ write!(f, "{}", segment.name.display(f.db.upcast()))?;
if let Some(generic_args) = segment.args_and_bindings {
// We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
// Do we actually format expressions?
@@ -1598,7 +1877,7 @@ impl HirDisplay for Path {
} else {
write!(f, ", ")?;
}
- write!(f, "{}", binding.name)?;
+ write!(f, "{}", binding.name.display(f.db.upcast()))?;
match &binding.type_ref {
Some(ty) => {
write!(f, " = ")?;
@@ -1621,8 +1900,10 @@ impl HirDisplay for hir_def::path::GenericArg {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
- hir_def::path::GenericArg::Const(c) => write!(f, "{c}"),
- hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
+ hir_def::path::GenericArg::Const(c) => write!(f, "{}", c.display(f.db.upcast())),
+ hir_def::path::GenericArg::Lifetime(lifetime) => {
+ write!(f, "{}", lifetime.name.display(f.db.upcast()))
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 7de5b4295..1ac0837b5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -13,34 +13,43 @@
//! to certain types. To record this, we use the union-find implementation from
//! the `ena` crate, which is extracted from rustc.
-use std::ops::Index;
-use std::sync::Arc;
+use std::{convert::identity, ops::Index};
-use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use chalk_ir::{
+ cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
+ Scalar, TyKind, TypeFlags,
+};
use either::Either;
use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
- expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
+ hir::LabelId,
+ hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
- path::Path,
+ path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
- ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
+ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
+ TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
-use la_arena::ArenaMap;
+use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
-use stdx::always;
+use stdx::{always, never};
+use triomphe::Arc;
use crate::{
- db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
- lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
- GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
- TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+ db::HirDatabase,
+ fold_tys,
+ infer::coerce::CoerceMany,
+ lower::ImplTraitLoweringMode,
+ static_lifetime, to_assoc_type_id,
+ traits::FnTrait,
+ utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
+ AliasEq, AliasTy, ClosureId, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment,
+ Interner, ProjectionTy, RpitId, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
};
// This lint has a false positive here. See the link below for details.
@@ -51,12 +60,15 @@ pub use coerce::could_coerce;
#[allow(unreachable_pub)]
pub use unify::could_unify;
+pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
+
pub(crate) mod unify;
mod path;
mod expr;
mod pat;
mod coerce;
-mod closure;
+pub(crate) mod closure;
+mod mutability;
/// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@@ -95,10 +107,24 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
},
});
}
+ DefWithBodyId::InTypeConstId(c) => {
+ // FIXME(const-generic-body): We should not get the return type in this way.
+ ctx.return_ty = c
+ .lookup(db.upcast())
+ .thing
+ .box_any()
+ .downcast::<InTypeConstIdMetadata>()
+ .unwrap()
+ .0;
+ }
}
ctx.infer_body();
+ ctx.infer_mut_body();
+
+ ctx.infer_closures();
+
Arc::new(ctx.resolve_all())
}
@@ -106,14 +132,15 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
///
/// This is appropriate to use only after type-check: it assumes
/// that normalization will succeed, for example.
-pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
- if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
+pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
+ // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only
+ // works for the type case, so we check array unconditionally. Remove the array part
+ // when the bug in chalk becomes fixed.
+ if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION)
+ && !matches!(ty.kind(Interner), TyKind::Array(..))
+ {
return ty;
}
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty);
@@ -188,7 +215,7 @@ pub enum InferenceDiagnostic {
/// Contains the type the field resolves to
field_with_same_name: Option<Ty>,
},
- // FIXME: Make this proper
+ // FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
expr: ExprId,
is_break: bool,
@@ -203,6 +230,10 @@ pub enum InferenceDiagnostic {
call_expr: ExprId,
found: Ty,
},
+ TypedHole {
+ expr: ExprId,
+ expected: Ty,
+ },
}
/// A mismatch between an expected and an inferred type.
@@ -276,6 +307,13 @@ pub struct Adjustment {
pub target: Ty,
}
+impl Adjustment {
+ pub fn borrow(m: Mutability, ty: Ty) -> Self {
+ let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
+ Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Adjust {
/// Go from ! to any type.
@@ -304,6 +342,13 @@ pub enum AutoBorrow {
RawPtr(Mutability),
}
+impl AutoBorrow {
+ fn mutability(self) -> Mutability {
+ let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
+ m
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast {
/// Go from a fn-item type to a fn-pointer type.
@@ -337,6 +382,10 @@ pub enum PointerCast {
}
/// The result of type inference: A mapping from expressions and patterns to types.
+///
+/// When you add a field that stores types (including `Substitution` and the like), don't forget
+/// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must
+/// not appear in the final inference result.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct InferenceResult {
/// For each method call expr, records the function it resolves to.
@@ -363,8 +412,11 @@ pub struct InferenceResult {
standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
- pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
+ pub binding_modes: ArenaMap<BindingId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
+ pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
+ // FIXME: remove this field
+ pub mutated_bindings_in_closure: FxHashSet<BindingId>,
}
impl InferenceResult {
@@ -401,6 +453,9 @@ impl InferenceResult {
_ => None,
})
}
+ pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
+ self.closure_info.get(closure).unwrap()
+ }
}
impl Index<ExprId> for InferenceResult {
@@ -435,7 +490,6 @@ pub(crate) struct InferenceContext<'a> {
pub(crate) body: &'a Body,
pub(crate) resolver: Resolver,
table: unify::InferenceTable<'a>,
- trait_env: Arc<TraitEnvironment>,
/// The traits in scope, disregarding block modules. This is used for caching purposes.
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
@@ -453,6 +507,14 @@ pub(crate) struct InferenceContext<'a> {
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
+
+ // fields related to closure capture
+ current_captures: Vec<CapturedItemWithoutTy>,
+ current_closure: Option<ClosureId>,
+ /// Stores the list of closure ids that need to be analyzed before this closure. See the
+ /// comment on `InferenceContext::sort_closures`
+ closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
+ deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
}
#[derive(Clone, Debug)]
@@ -462,7 +524,7 @@ struct BreakableContext {
/// The coercion target of the context.
coerce: Option<CoerceMany>,
/// The optional label of the context.
- label: Option<name::Name>,
+ label: Option<LabelId>,
kind: BreakableKind,
}
@@ -477,21 +539,21 @@ enum BreakableKind {
fn find_breakable<'c>(
ctxs: &'c mut [BreakableContext],
- label: Option<&name::Name>,
+ label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
let mut ctxs = ctxs
.iter_mut()
.rev()
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
match label {
- Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
+ Some(_) => ctxs.find(|ctx| ctx.label == label),
None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
}
}
fn find_continuable<'c>(
ctxs: &'c mut [BreakableContext],
- label: Option<&name::Name>,
+ label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
@@ -506,14 +568,10 @@ impl<'a> InferenceContext<'a> {
body: &'a Body,
resolver: Resolver,
) -> Self {
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
+ let trait_env = db.trait_environment_for_body(owner);
InferenceContext {
result: InferenceResult::default(),
- table: unify::InferenceTable::new(db, trait_env.clone()),
- trait_env,
+ table: unify::InferenceTable::new(db, trait_env),
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
resume_yield_tys: None,
return_coercion: None,
@@ -524,6 +582,10 @@ impl<'a> InferenceContext<'a> {
resolver,
diverges: Diverges::Maybe,
breakables: Vec::new(),
+ current_captures: vec![],
+ current_closure: None,
+ deferred_closures: FxHashMap::default(),
+ closure_dependencies: FxHashMap::default(),
}
}
@@ -533,6 +595,30 @@ impl<'a> InferenceContext<'a> {
// there is no problem in it being `pub(crate)`, remove this comment.
pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self;
+ // Destructure every single field so whenever new fields are added to `InferenceResult` we
+ // don't forget to handle them here.
+ let InferenceResult {
+ method_resolutions,
+ field_resolutions: _,
+ variant_resolutions: _,
+ assoc_resolutions,
+ diagnostics,
+ type_of_expr,
+ type_of_pat,
+ type_of_binding,
+ type_of_rpit,
+ type_of_for_iterator,
+ type_mismatches,
+ standard_types: _,
+ pat_adjustments,
+ binding_modes: _,
+ expr_adjustments,
+ // Types in `closure_info` have already been `resolve_completely()`'d during
+ // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically), so no need
+ // to resolve them here.
+ closure_info: _,
+ mutated_bindings_in_closure: _,
+ } = &mut result;
table.fallback_if_possible();
@@ -541,62 +627,63 @@ impl<'a> InferenceContext<'a> {
// make sure diverging type variables are marked as such
table.propagate_diverging_flag();
- for ty in result.type_of_expr.values_mut() {
+ for ty in type_of_expr.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_pat.values_mut() {
+ for ty in type_of_pat.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_binding.values_mut() {
+ for ty in type_of_binding.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_rpit.values_mut() {
+ for ty in type_of_rpit.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_for_iterator.values_mut() {
+ for ty in type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for mismatch in result.type_mismatches.values_mut() {
+ for mismatch in type_mismatches.values_mut() {
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
}
- result.diagnostics.retain_mut(|diagnostic| {
- if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
- | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
- | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
- {
- *ty = table.resolve_completely(ty.clone());
- // FIXME: Remove this when we are on par with rustc in terms of inference
- if ty.contains_unknown() {
- return false;
- }
+ diagnostics.retain_mut(|diagnostic| {
+ use InferenceDiagnostic::*;
+ match diagnostic {
+ ExpectedFunction { found: ty, .. }
+ | UnresolvedField { receiver: ty, .. }
+ | UnresolvedMethodCall { receiver: ty, .. } => {
+ *ty = table.resolve_completely(ty.clone());
+ // FIXME: Remove this when we are on par with rustc in terms of inference
+ if ty.contains_unknown() {
+ return false;
+ }
- if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
- diagnostic
- {
- let clear = if let Some(ty) = field_with_same_name {
- *ty = table.resolve_completely(ty.clone());
- ty.contains_unknown()
- } else {
- false
- };
- if clear {
- *field_with_same_name = None;
+ if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic {
+ if let Some(ty) = field_with_same_name {
+ *ty = table.resolve_completely(ty.clone());
+ if ty.contains_unknown() {
+ *field_with_same_name = None;
+ }
+ }
}
}
+ TypedHole { expected: ty, .. } => {
+ *ty = table.resolve_completely(ty.clone());
+ }
+ _ => (),
}
true
});
- for (_, subst) in result.method_resolutions.values_mut() {
+ for (_, subst) in method_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
- for (_, subst) in result.assoc_resolutions.values_mut() {
+ for (_, subst) in assoc_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
- for adjustment in result.expr_adjustments.values_mut().flatten() {
+ for adjustment in expr_adjustments.values_mut().flatten() {
adjustment.target = table.resolve_completely(adjustment.target.clone());
}
- for adjustment in result.pat_adjustments.values_mut().flatten() {
+ for adjustment in pat_adjustments.values_mut().flatten() {
*adjustment = table.resolve_completely(adjustment.clone());
}
result
@@ -612,10 +699,10 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, func.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Param);
let mut param_tys =
- data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
+ data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
// Check if function contains a va_list, if it does then we append it to the parameter types
// that are collected from the function data
if data.is_varargs() {
@@ -634,14 +721,9 @@ impl<'a> InferenceContext<'a> {
self.infer_top_pat(*pat, &ty);
}
- let error_ty = &TypeRef::Error;
- let return_ty = if data.has_async_kw() {
- data.async_ret_type.as_deref().unwrap_or(error_ty)
- } else {
- &*data.ret_type
- };
+ let return_ty = &*data.ret_type;
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
let return_ty = ctx.lower_ty(return_ty);
let return_ty = self.insert_type_vars(return_ty);
@@ -649,36 +731,16 @@ impl<'a> InferenceContext<'a> {
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
- fold_tys(
- return_ty,
- |ty, _| {
- let opaque_ty_id = match ty.kind(Interner) {
- TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
- _ => return ty,
- };
- let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
- ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
- _ => unreachable!(),
- };
- let bounds = (*rpits).map_ref(|rpits| {
- rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
- });
- let var = self.table.new_type_var();
- let var_subst = Substitution::from1(Interner, var.clone());
- for bound in bounds {
- let predicate =
- bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
- let (var_predicate, binders) = predicate
- .substitute(Interner, &var_subst)
- .into_value_and_skipped_binders();
- always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
- self.push_obligation(var_predicate.cast(Interner));
- }
- self.result.type_of_rpit.insert(idx, var.clone());
- var
- },
- DebruijnIndex::INNERMOST,
- )
+ let result =
+ self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
+ let rpits = rpits.skip_binders();
+ for (id, _) in rpits.impl_traits.iter() {
+ if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
+ never!("Missed RPIT in `insert_inference_vars_for_rpit`");
+ e.insert(TyKind::Error.intern(Interner));
+ }
+ }
+ result
} else {
return_ty
};
@@ -687,6 +749,50 @@ impl<'a> InferenceContext<'a> {
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
}
+ fn insert_inference_vars_for_rpit<T>(
+ &mut self,
+ t: T,
+ rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
+ fn_placeholders: Substitution,
+ ) -> T
+ where
+ T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
+ {
+ fold_tys(
+ t,
+ |ty, _| {
+ let opaque_ty_id = match ty.kind(Interner) {
+ TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
+ _ => return ty,
+ };
+ let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+ ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
+ _ => unreachable!(),
+ };
+ let bounds = (*rpits)
+ .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
+ let var = self.table.new_type_var();
+ let var_subst = Substitution::from1(Interner, var.clone());
+ for bound in bounds {
+ let predicate =
+ bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
+ let (var_predicate, binders) =
+ predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
+ always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
+ let var_predicate = self.insert_inference_vars_for_rpit(
+ var_predicate,
+ rpits.clone(),
+ fn_placeholders.clone(),
+ );
+ self.push_obligation(var_predicate.cast(Interner));
+ }
+ self.result.type_of_rpit.insert(idx, var.clone());
+ var
+ },
+ DebruijnIndex::INNERMOST,
+ )
+ }
+
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),
@@ -732,7 +838,7 @@ impl<'a> InferenceContext<'a> {
}
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let ty = ctx.lower_ty(type_ref);
let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty)
@@ -742,43 +848,16 @@ impl<'a> InferenceContext<'a> {
self.result.standard_types.unknown.clone()
}
- /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
- fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
- let data = c.data(Interner);
- match &data.value {
- ConstValue::Concrete(cc) => match cc.interned {
- crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
- _ => c,
- },
- _ => c,
- }
- }
-
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
- match ty.kind(Interner) {
- TyKind::Error => self.table.new_type_var(),
- TyKind::InferenceVar(..) => {
- let ty_resolved = self.resolve_ty_shallow(&ty);
- if ty_resolved.is_unknown() {
- self.table.new_type_var()
- } else {
- ty
- }
- }
- _ => ty,
- }
+ self.table.insert_type_vars_shallow(ty)
}
- fn insert_type_vars(&mut self, ty: Ty) -> Ty {
- fold_tys_and_consts(
- ty,
- |x, _| match x {
- Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
- Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
- },
- DebruijnIndex::INNERMOST,
- )
+ fn insert_type_vars<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ self.table.insert_type_vars(ty)
}
fn push_obligation(&mut self, o: DomainGoal) {
@@ -786,7 +865,80 @@ impl<'a> InferenceContext<'a> {
}
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
- self.table.unify(ty1, ty2)
+ let ty1 = ty1
+ .clone()
+ .try_fold_with(
+ &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+ DebruijnIndex::INNERMOST,
+ )
+ .unwrap();
+ let ty2 = ty2
+ .clone()
+ .try_fold_with(
+ &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+ DebruijnIndex::INNERMOST,
+ )
+ .unwrap();
+ self.table.unify(&ty1, &ty2)
+ }
+
+ /// Attempts to returns the deeply last field of nested structures, but
+ /// does not apply any normalization in its search. Returns the same type
+ /// if input `ty` is not a structure at all.
+ fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty {
+ self.struct_tail_with_normalize(ty, identity)
+ }
+
+ /// Returns the deeply last field of nested structures, or the same type if
+ /// not a structure at all. Corresponds to the only possible unsized field,
+ /// and its type can be used to determine unsizing strategy.
+ ///
+ /// This is parameterized over the normalization strategy (i.e. how to
+ /// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
+ /// function to indicate no normalization should take place.
+ fn struct_tail_with_normalize(
+ &mut self,
+ mut ty: Ty,
+ mut normalize: impl FnMut(Ty) -> Ty,
+ ) -> Ty {
+ // FIXME: fetch the limit properly
+ let recursion_limit = 10;
+ for iteration in 0.. {
+ if iteration > recursion_limit {
+ return self.err_ty();
+ }
+ match ty.kind(Interner) {
+ TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => {
+ match self.db.field_types((*struct_id).into()).values().next_back().cloned() {
+ Some(field) => {
+ ty = field.substitute(Interner, substs);
+ }
+ None => break,
+ }
+ }
+ TyKind::Adt(..) => break,
+ TyKind::Tuple(_, substs) => {
+ match substs
+ .as_slice(Interner)
+ .split_last()
+ .and_then(|(last_ty, _)| last_ty.ty(Interner))
+ {
+ Some(last_ty) => ty = last_ty.clone(),
+ None => break,
+ }
+ }
+ TyKind::Alias(..) => {
+ let normalized = normalize(ty.clone());
+ if ty == normalized {
+ return ty;
+ } else {
+ ty = normalized;
+ }
+ }
+ _ => break,
+ }
+ }
+ ty
}
/// Recurses through the given type, normalizing associated types mentioned
@@ -795,7 +947,10 @@ impl<'a> InferenceContext<'a> {
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
- fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
+ fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
self.table.normalize_associated_types_in(ty)
}
@@ -847,11 +1002,9 @@ impl<'a> InferenceContext<'a> {
Some(path) => path,
None => return (self.err_ty(), None),
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- // FIXME: this should resolve assoc items as well, see this example:
- // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let (resolution, unresolved) = if value_ns {
- match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
Some(ResolveValueResult::ValueNs(value)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
@@ -872,11 +1025,15 @@ impl<'a> InferenceContext<'a> {
None => return (self.err_ty(), None),
}
} else {
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (self.err_ty(), None),
}
};
+ let Some(mod_path) = path.mod_path() else {
+ never!("resolver should always resolve lang item paths");
+ return (self.err_ty(), None);
+ };
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
@@ -899,8 +1056,68 @@ impl<'a> InferenceContext<'a> {
TypeNs::SelfType(impl_id) => {
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
let substs = generics.placeholder_subst(self.db);
- let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
- self.resolve_variant_on_alias(ty, unresolved, path)
+ let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
+
+ let Some(mut remaining_idx) = unresolved else {
+ return self.resolve_variant_on_alias(ty, None, mod_path);
+ };
+
+ let mut remaining_segments = path.segments().skip(remaining_idx);
+
+ // We need to try resolving unresolved segments one by one because each may resolve
+ // to a projection, which `TyLoweringContext` cannot handle on its own.
+ while !remaining_segments.is_empty() {
+ let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
+ let current_segment = remaining_segments.take(1);
+
+ // If we can resolve to an enum variant, it takes priority over associated type
+ // of the same name.
+ if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
+ let enum_data = self.db.enum_data(id);
+ let name = current_segment.first().unwrap().name;
+ if let Some(local_id) = enum_data.variant(name) {
+ let variant = EnumVariantId { parent: id, local_id };
+ return if remaining_segments.len() == 1 {
+ (ty, Some(variant.into()))
+ } else {
+ // We still have unresolved paths, but enum variants never have
+ // associated types!
+ (self.err_ty(), None)
+ };
+ }
+ }
+
+ // `lower_partly_resolved_path()` returns `None` as type namespace unless
+ // `remaining_segments` is empty, which is never the case here. We don't know
+ // which namespace the new `ty` is in until normalized anyway.
+ (ty, _) = ctx.lower_partly_resolved_path(
+ resolution,
+ resolved_segment,
+ current_segment,
+ false,
+ );
+
+ ty = self.table.insert_type_vars(ty);
+ ty = self.table.normalize_associated_types_in(ty);
+ ty = self.table.resolve_ty_shallow(&ty);
+ if ty.is_unknown() {
+ return (self.err_ty(), None);
+ }
+
+ // FIXME(inherent_associated_types): update `resolution` based on `ty` here.
+ remaining_idx += 1;
+ remaining_segments = remaining_segments.skip(1);
+ }
+
+ let variant = ty.as_adt().and_then(|(id, _)| match id {
+ AdtId::StructId(s) => Some(VariantId::StructId(s)),
+ AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
+ AdtId::EnumId(_) => {
+ // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
+ None
+ }
+ });
+ (ty, variant)
}
TypeNs::TypeAliasId(it) => {
let container = it.lookup(self.db.upcast()).container;
@@ -917,7 +1134,7 @@ impl<'a> InferenceContext<'a> {
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table)
.build();
- self.resolve_variant_on_alias(ty, unresolved, path)
+ self.resolve_variant_on_alias(ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them
@@ -953,9 +1170,9 @@ impl<'a> InferenceContext<'a> {
&mut self,
ty: Ty,
unresolved: Option<usize>,
- path: &Path,
+ path: &ModPath,
) -> (Ty, Option<VariantId>) {
- let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0);
+ let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0);
match remaining {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@@ -969,7 +1186,7 @@ impl<'a> InferenceContext<'a> {
(ty, variant)
}
Some(1) => {
- let segment = path.mod_path().segments().last().unwrap();
+ let segment = path.segments().last().unwrap();
// this could be an enum variant or associated type
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id);
@@ -993,22 +1210,6 @@ impl<'a> InferenceContext<'a> {
self.db.lang_item(krate, item)
}
- fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
- let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
- .as_function()?
- .lookup(self.db.upcast()).container
- else { return None };
- self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
- }
-
- fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
- let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)?
- .as_function()?
- .lookup(self.db.upcast()).container
- else { return None };
- self.db.trait_data(trait_).associated_type_by_name(&name![Item])
- }
-
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
}
@@ -1017,10 +1218,6 @@ impl<'a> InferenceContext<'a> {
self.resolve_lang_item(lang)?.as_trait()
}
- fn resolve_ops_try_output(&self) -> Option<TypeAliasId> {
- self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?)
- }
-
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
}
@@ -1136,9 +1333,8 @@ impl Expectation {
/// which still is useful, because it informs integer literals and the like.
/// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
- fn rvalue_hint(table: &mut unify::InferenceTable<'_>, ty: Ty) -> Self {
- // FIXME: do struct_tail_without_normalization
- match table.resolve_ty_shallow(&ty).kind(Interner) {
+ fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self {
+ match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) {
TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
_ => Expectation::has_type(ty),
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index a6449d019..ff64ae252 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -1,12 +1,33 @@
//! Inference of closure parameter types based on the closure's expected type.
-use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause};
-use hir_def::{expr::ExprId, HasModule};
+use std::{cmp, collections::HashMap, convert::Infallible, mem};
+
+use chalk_ir::{
+ cast::Cast,
+ fold::{FallibleTypeFolder, TypeFoldable},
+ AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
+};
+use hir_def::{
+ data::adt::VariantData,
+ hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
+ lang_item::LangItem,
+ resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+ DefWithBodyId, FieldId, HasModule, VariantId,
+};
+use hir_expand::name;
+use rustc_hash::FxHashMap;
use smallvec::SmallVec;
+use stdx::never;
use crate::{
- to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty,
- TyExt, TyKind,
+ db::HirDatabase,
+ from_placeholder_idx, make_binders,
+ mir::{BorrowKind, MirSpan, ProjectionElem},
+ static_lifetime, to_chalk_trait_id,
+ traits::FnTrait,
+ utils::{self, generics, Generics},
+ Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig,
+ Interner, Substitution, Ty, TyExt,
};
use super::{Expectation, InferenceContext};
@@ -86,3 +107,905 @@ impl InferenceContext<'_> {
None
}
}
+
+// The below functions handle capture and closure kind (Fn, FnMut, ..)
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct HirPlace {
+ pub(crate) local: BindingId,
+ pub(crate) projections: Vec<ProjectionElem<Infallible, Ty>>,
+}
+
+impl HirPlace {
+ fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
+ let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
+ for p in &self.projections {
+ ty = p.projected_ty(
+ ty,
+ ctx.db,
+ |_, _, _| {
+ unreachable!("Closure field only happens in MIR");
+ },
+ ctx.owner.module(ctx.db.upcast()).krate(),
+ );
+ }
+ ty.clone()
+ }
+
+ fn capture_kind_of_truncated_place(
+ &self,
+ mut current_capture: CaptureKind,
+ len: usize,
+ ) -> CaptureKind {
+ match current_capture {
+ CaptureKind::ByRef(BorrowKind::Mut { .. }) => {
+ if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) {
+ current_capture = CaptureKind::ByRef(BorrowKind::Unique);
+ }
+ }
+ _ => (),
+ }
+ current_capture
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CaptureKind {
+ ByRef(BorrowKind),
+ ByValue,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CapturedItem {
+ pub(crate) place: HirPlace,
+ pub(crate) kind: CaptureKind,
+ pub(crate) span: MirSpan,
+ pub(crate) ty: Binders<Ty>,
+}
+
+impl CapturedItem {
+ pub fn local(&self) -> BindingId {
+ self.place.local
+ }
+
+ pub fn ty(&self, subst: &Substitution) -> Ty {
+ self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst())
+ }
+
+ pub fn kind(&self) -> CaptureKind {
+ self.kind
+ }
+
+ pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
+ let body = db.body(owner);
+ let mut result = body[self.place.local].name.display(db.upcast()).to_string();
+ let mut field_need_paren = false;
+ for proj in &self.place.projections {
+ match proj {
+ ProjectionElem::Deref => {
+ result = format!("*{result}");
+ field_need_paren = true;
+ }
+ ProjectionElem::Field(f) => {
+ if field_need_paren {
+ result = format!("({result})");
+ }
+ let variant_data = f.parent.variant_data(db.upcast());
+ let field = match &*variant_data {
+ VariantData::Record(fields) => fields[f.local_id]
+ .name
+ .as_str()
+ .unwrap_or("[missing field]")
+ .to_string(),
+ VariantData::Tuple(fields) => fields
+ .iter()
+ .position(|x| x.0 == f.local_id)
+ .unwrap_or_default()
+ .to_string(),
+ VariantData::Unit => "[missing field]".to_string(),
+ };
+ result = format!("{result}.{field}");
+ field_need_paren = false;
+ }
+ &ProjectionElem::TupleOrClosureField(field) => {
+ if field_need_paren {
+ result = format!("({result})");
+ }
+ result = format!("{result}.{field}");
+ field_need_paren = false;
+ }
+ ProjectionElem::Index(_)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::OpaqueCast(_) => {
+ never!("Not happen in closure capture");
+ continue;
+ }
+ }
+ }
+ result
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct CapturedItemWithoutTy {
+ pub(crate) place: HirPlace,
+ pub(crate) kind: CaptureKind,
+ pub(crate) span: MirSpan,
+}
+
+impl CapturedItemWithoutTy {
+ fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
+ let ty = self.place.ty(ctx).clone();
+ let ty = match &self.kind {
+ CaptureKind::ByValue => ty,
+ CaptureKind::ByRef(bk) => {
+ let m = match bk {
+ BorrowKind::Mut { .. } => Mutability::Mut,
+ _ => Mutability::Not,
+ };
+ TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
+ }
+ };
+ return CapturedItem {
+ place: self.place,
+ kind: self.kind,
+ span: self.span,
+ ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty),
+ };
+
+ fn replace_placeholder_with_binder(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ ty: Ty,
+ ) -> Binders<Ty> {
+ struct Filler<'a> {
+ db: &'a dyn HirDatabase,
+ generics: Generics,
+ }
+ impl FallibleTypeFolder<Interner> for Filler<'_> {
+ type Error = ();
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_free_placeholder_const(
+ &mut self,
+ ty: chalk_ir::Ty<Interner>,
+ idx: chalk_ir::PlaceholderIndex,
+ outer_binder: DebruijnIndex,
+ ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.param_idx(x) else {
+ return Err(());
+ };
+ Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty))
+ }
+
+ fn try_fold_free_placeholder_ty(
+ &mut self,
+ idx: chalk_ir::PlaceholderIndex,
+ outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.param_idx(x) else {
+ return Err(());
+ };
+ Ok(BoundVar::new(outer_binder, idx).to_ty(Interner))
+ }
+ }
+ let Some(generic_def) = owner.as_generic_def_id() else {
+ return Binders::empty(Interner, ty);
+ };
+ let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) };
+ let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
+ make_binders(db, &filler.generics, result)
+ }
+ }
+}
+
+impl InferenceContext<'_> {
+ fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+ let r = self.place_of_expr_without_adjust(tgt_expr)?;
+ let default = vec![];
+ let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
+ apply_adjusts_to_place(r, adjustments)
+ }
+
+ fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+ match &self.body[tgt_expr] {
+ Expr::Path(p) => {
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
+ if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
+ if let ResolveValueResult::ValueNs(v) = r {
+ if let ValueNs::LocalBinding(b) = v {
+ return Some(HirPlace { local: b, projections: vec![] });
+ }
+ }
+ }
+ }
+ Expr::Field { expr, name } => {
+ let mut place = self.place_of_expr(*expr)?;
+ if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
+ let index = name.as_tuple_index()?;
+ place.projections.push(ProjectionElem::TupleOrClosureField(index))
+ } else {
+ let field = self.result.field_resolution(tgt_expr)?;
+ place.projections.push(ProjectionElem::Field(field));
+ }
+ return Some(place);
+ }
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if matches!(
+ self.expr_ty_after_adjustments(*expr).kind(Interner),
+ TyKind::Ref(..) | TyKind::Raw(..)
+ ) {
+ let mut place = self.place_of_expr(*expr)?;
+ place.projections.push(ProjectionElem::Deref);
+ return Some(place);
+ }
+ }
+ _ => (),
+ }
+ None
+ }
+
+ fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
+ self.current_captures.push(capture);
+ }
+
+ fn ref_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into());
+ }
+ self.walk_expr(expr);
+ }
+
+ fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) {
+ if self.is_upvar(&place) {
+ self.push_capture(CapturedItemWithoutTy { place, kind, span });
+ }
+ }
+
+ fn mutate_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.add_capture(
+ place,
+ CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ expr.into(),
+ );
+ }
+ self.walk_expr(expr);
+ }
+
+ fn consume_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.consume_place(place, expr.into());
+ }
+ self.walk_expr(expr);
+ }
+
+ fn consume_place(&mut self, place: HirPlace, span: MirSpan) {
+ if self.is_upvar(&place) {
+ let ty = place.ty(self).clone();
+ let kind = if self.is_ty_copy(ty) {
+ CaptureKind::ByRef(BorrowKind::Shared)
+ } else {
+ CaptureKind::ByValue
+ };
+ self.push_capture(CapturedItemWithoutTy { place, kind, span });
+ }
+ }
+
+ fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
+ if let Some((last, rest)) = adjustment.split_last() {
+ match last.kind {
+ Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
+ self.walk_expr_with_adjust(tgt_expr, rest)
+ }
+ Adjust::Deref(Some(m)) => match m.0 {
+ Some(m) => {
+ self.ref_capture_with_adjusts(m, tgt_expr, rest);
+ }
+ None => unreachable!(),
+ },
+ Adjust::Borrow(b) => {
+ self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
+ }
+ }
+ } else {
+ self.walk_expr_without_adjust(tgt_expr);
+ }
+ }
+
+ fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
+ let capture_kind = match m {
+ Mutability::Mut => {
+ CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
+ }
+ Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
+ };
+ if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
+ if let Some(place) = apply_adjusts_to_place(place, rest) {
+ self.add_capture(place, capture_kind, tgt_expr.into());
+ }
+ }
+ self.walk_expr_with_adjust(tgt_expr, rest);
+ }
+
+ fn walk_expr(&mut self, tgt_expr: ExprId) {
+ if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
+ // FIXME: this take is completely unneeded, and just is here to make borrow checker
+ // happy. Remove it if you can.
+ let x_taken = mem::take(x);
+ self.walk_expr_with_adjust(tgt_expr, &x_taken);
+ *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
+ } else {
+ self.walk_expr_without_adjust(tgt_expr);
+ }
+ }
+
+ fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
+ match &self.body[tgt_expr] {
+ Expr::If { condition, then_branch, else_branch } => {
+ self.consume_expr(*condition);
+ self.consume_expr(*then_branch);
+ if let &Some(expr) = else_branch {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::Async { statements, tail, .. }
+ | Expr::Unsafe { statements, tail, .. }
+ | Expr::Block { statements, tail, .. } => {
+ for s in statements.iter() {
+ match s {
+ Statement::Let { pat, type_ref: _, initializer, else_branch } => {
+ if let Some(else_branch) = else_branch {
+ self.consume_expr(*else_branch);
+ if let Some(initializer) = initializer {
+ self.consume_expr(*initializer);
+ }
+ return;
+ }
+ if let Some(initializer) = initializer {
+ self.walk_expr(*initializer);
+ if let Some(place) = self.place_of_expr(*initializer) {
+ self.consume_with_pat(place, *pat);
+ }
+ }
+ }
+ Statement::Expr { expr, has_semi: _ } => {
+ self.consume_expr(*expr);
+ }
+ }
+ }
+ if let Some(tail) = tail {
+ self.consume_expr(*tail);
+ }
+ }
+ Expr::While { condition, body, label: _ } => {
+ self.consume_expr(*condition);
+ self.consume_expr(*body);
+ }
+ Expr::Call { callee, args, is_assignee_expr: _ } => {
+ self.consume_expr(*callee);
+ self.consume_exprs(args.iter().copied());
+ }
+ Expr::MethodCall { receiver, args, .. } => {
+ self.consume_expr(*receiver);
+ self.consume_exprs(args.iter().copied());
+ }
+ Expr::Match { expr, arms } => {
+ for arm in arms.iter() {
+ self.consume_expr(arm.expr);
+ if let Some(guard) = arm.guard {
+ self.consume_expr(guard);
+ }
+ }
+ self.walk_expr(*expr);
+ if let Some(discr_place) = self.place_of_expr(*expr) {
+ if self.is_upvar(&discr_place) {
+ let mut capture_mode = None;
+ for arm in arms.iter() {
+ self.walk_pat(&mut capture_mode, arm.pat);
+ }
+ if let Some(c) = capture_mode {
+ self.push_capture(CapturedItemWithoutTy {
+ place: discr_place,
+ kind: c,
+ span: (*expr).into(),
+ })
+ }
+ }
+ }
+ }
+ Expr::Break { expr, label: _ }
+ | Expr::Return { expr }
+ | Expr::Yield { expr }
+ | Expr::Yeet { expr } => {
+ if let &Some(expr) = expr {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::RecordLit { fields, spread, .. } => {
+ if let &Some(expr) = spread {
+ self.consume_expr(expr);
+ }
+ self.consume_exprs(fields.iter().map(|x| x.expr));
+ }
+ Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if matches!(
+ self.expr_ty_after_adjustments(*expr).kind(Interner),
+ TyKind::Ref(..) | TyKind::Raw(..)
+ ) {
+ self.select_from_expr(*expr);
+ } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
+ let mutability = 'b: {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
+ {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ false
+ };
+ if mutability {
+ self.mutate_expr(*expr);
+ } else {
+ self.ref_expr(*expr);
+ }
+ } else {
+ self.select_from_expr(*expr);
+ }
+ }
+ Expr::UnaryOp { expr, op: _ }
+ | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
+ | Expr::Await { expr }
+ | Expr::Loop { body: expr, label: _ }
+ | Expr::Let { pat: _, expr }
+ | Expr::Box { expr }
+ | Expr::Cast { expr, type_ref: _ } => {
+ self.consume_expr(*expr);
+ }
+ Expr::Ref { expr, rawness: _, mutability } => match mutability {
+ hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
+ hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
+ },
+ Expr::BinaryOp { lhs, rhs, op } => {
+ let Some(op) = op else {
+ return;
+ };
+ if matches!(op, BinaryOp::Assignment { .. }) {
+ self.mutate_expr(*lhs);
+ self.consume_expr(*rhs);
+ return;
+ }
+ self.consume_expr(*lhs);
+ self.consume_expr(*rhs);
+ }
+ Expr::Range { lhs, rhs, range_type: _ } => {
+ if let &Some(expr) = lhs {
+ self.consume_expr(expr);
+ }
+ if let &Some(expr) = rhs {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::Index { base, index } => {
+ self.select_from_expr(*base);
+ self.consume_expr(*index);
+ }
+ Expr::Closure { .. } => {
+ let ty = self.expr_ty(tgt_expr);
+ let TyKind::Closure(id, _) = ty.kind(Interner) else {
+ never!("closure type is always closure");
+ return;
+ };
+ let (captures, _) =
+ self.result.closure_info.get(id).expect(
+ "We sort closures, so we should always have data for inner closures",
+ );
+ let mut cc = mem::take(&mut self.current_captures);
+ cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| {
+ CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span }
+ }));
+ self.current_captures = cc;
+ }
+ Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
+ | Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ self.consume_exprs(exprs.iter().copied())
+ }
+ Expr::Missing
+ | Expr::Continue { .. }
+ | Expr::Path(_)
+ | Expr::Literal(_)
+ | Expr::Const(_)
+ | Expr::Underscore => (),
+ }
+ }
+
+ fn walk_pat(&mut self, result: &mut Option<CaptureKind>, pat: PatId) {
+ let mut update_result = |ck: CaptureKind| match result {
+ Some(r) => {
+ *r = cmp::max(*r, ck);
+ }
+ None => *result = Some(ck),
+ };
+
+ self.walk_pat_inner(
+ pat,
+ &mut update_result,
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ );
+ }
+
+ fn walk_pat_inner(
+ &mut self,
+ p: PatId,
+ update_result: &mut impl FnMut(CaptureKind),
+ mut for_mut: BorrowKind,
+ ) {
+ match &self.body[p] {
+ Pat::Ref { .. }
+ | Pat::Box { .. }
+ | Pat::Missing
+ | Pat::Wild
+ | Pat::Tuple { .. }
+ | Pat::Or(_) => (),
+ Pat::TupleStruct { .. } | Pat::Record { .. } => {
+ if let Some(variant) = self.result.variant_resolution_for_pat(p) {
+ let adt = variant.adt_id();
+ let is_multivariant = match adt {
+ hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1,
+ _ => false,
+ };
+ if is_multivariant {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ }
+ }
+ }
+ Pat::Slice { .. }
+ | Pat::ConstBlock(_)
+ | Pat::Path(_)
+ | Pat::Lit(_)
+ | Pat::Range { .. } => {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ }
+ Pat::Bind { id, .. } => match self.result.binding_modes[*id] {
+ crate::BindingMode::Move => {
+ if self.is_ty_copy(self.result.type_of_binding[*id].clone()) {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ } else {
+ update_result(CaptureKind::ByValue);
+ }
+ }
+ crate::BindingMode::Ref(r) => match r {
+ Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)),
+ Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
+ },
+ },
+ }
+ if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) {
+ for_mut = BorrowKind::Unique;
+ }
+ self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
+ }
+
+ fn expr_ty(&self, expr: ExprId) -> Ty {
+ self.result[expr].clone()
+ }
+
+ fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
+ let mut ty = None;
+ if let Some(x) = self.result.expr_adjustments.get(&e) {
+ if let Some(x) = x.last() {
+ ty = Some(x.target.clone());
+ }
+ }
+ ty.unwrap_or_else(|| self.expr_ty(e))
+ }
+
+ fn is_upvar(&self, place: &HirPlace) -> bool {
+ if let Some(c) = self.current_closure {
+ let (_, root) = self.db.lookup_intern_closure(c.into());
+ return self.body.is_binding_upvar(place.local, root);
+ }
+ false
+ }
+
+ fn is_ty_copy(&mut self, ty: Ty) -> bool {
+ if let TyKind::Closure(id, _) = ty.kind(Interner) {
+ // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
+ // should probably let chalk know which closures are copy, but I don't know how doing it
+ // without creating query cycles.
+ return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
+ }
+ self.table.resolve_completely(ty).is_copy(self.db, self.owner)
+ }
+
+ fn select_from_expr(&mut self, expr: ExprId) {
+ self.walk_expr(expr);
+ }
+
+ fn adjust_for_move_closure(&mut self) {
+ for capture in &mut self.current_captures {
+ if let Some(first_deref) =
+ capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
+ {
+ capture.place.projections.truncate(first_deref);
+ }
+ capture.kind = CaptureKind::ByValue;
+ }
+ }
+
+ fn minimize_captures(&mut self) {
+ self.current_captures.sort_by_key(|x| x.place.projections.len());
+ let mut hash_map = HashMap::<HirPlace, usize>::new();
+ let result = mem::take(&mut self.current_captures);
+ for item in result {
+ let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
+ let mut it = item.place.projections.iter();
+ let prev_index = loop {
+ if let Some(k) = hash_map.get(&lookup_place) {
+ break Some(*k);
+ }
+ match it.next() {
+ Some(x) => lookup_place.projections.push(x.clone()),
+ None => break None,
+ }
+ };
+ match prev_index {
+ Some(p) => {
+ let len = self.current_captures[p].place.projections.len();
+ let kind_after_truncate =
+ item.place.capture_kind_of_truncated_place(item.kind, len);
+ self.current_captures[p].kind =
+ cmp::max(kind_after_truncate, self.current_captures[p].kind);
+ }
+ None => {
+ hash_map.insert(item.place.clone(), self.current_captures.len());
+ self.current_captures.push(item);
+ }
+ }
+ }
+ }
+
+ fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) {
+ let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default();
+ place.projections = place
+ .projections
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into();
+ match &self.body[pat] {
+ Pat::Missing | Pat::Wild => (),
+ Pat::Tuple { args, ellipsis } => {
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let field_count = match self.result[pat].kind(Interner) {
+ TyKind::Tuple(_, s) => s.len(Interner),
+ _ => return,
+ };
+ let fields = 0..field_count;
+ let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+ for (arg, i) in it {
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::TupleOrClosureField(i));
+ self.consume_with_pat(p, *arg);
+ }
+ }
+ Pat::Or(pats) => {
+ for pat in pats.iter() {
+ self.consume_with_pat(place.clone(), *pat);
+ }
+ }
+ Pat::Record { args, .. } => {
+ let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+ return;
+ };
+ match variant {
+ VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+ self.consume_place(place, pat.into())
+ }
+ VariantId::StructId(s) => {
+ let vd = &*self.db.struct_data(s).variant_data;
+ for field_pat in args.iter() {
+ let arg = field_pat.pat;
+ let Some(local_id) = vd.field(&field_pat.name) else {
+ continue;
+ };
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::Field(FieldId {
+ parent: variant.into(),
+ local_id,
+ }));
+ self.consume_with_pat(p, arg);
+ }
+ }
+ }
+ }
+ Pat::Range { .. }
+ | Pat::Slice { .. }
+ | Pat::ConstBlock(_)
+ | Pat::Path(_)
+ | Pat::Lit(_) => self.consume_place(place, pat.into()),
+ Pat::Bind { id, subpat: _ } => {
+ let mode = self.result.binding_modes[*id];
+ let capture_kind = match mode {
+ BindingMode::Move => {
+ self.consume_place(place, pat.into());
+ return;
+ }
+ BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
+ BindingMode::Ref(Mutability::Mut) => {
+ BorrowKind::Mut { allow_two_phase_borrow: false }
+ }
+ };
+ self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
+ }
+ Pat::TupleStruct { path: _, args, ellipsis } => {
+ let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+ return;
+ };
+ match variant {
+ VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+ self.consume_place(place, pat.into())
+ }
+ VariantId::StructId(s) => {
+ let vd = &*self.db.struct_data(s).variant_data;
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let fields = vd.fields().iter();
+ let it =
+ al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+ for (arg, (i, _)) in it {
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::Field(FieldId {
+ parent: variant.into(),
+ local_id: i,
+ }));
+ self.consume_with_pat(p, *arg);
+ }
+ }
+ }
+ }
+ Pat::Ref { pat, mutability: _ } => {
+ place.projections.push(ProjectionElem::Deref);
+ self.consume_with_pat(place, *pat)
+ }
+ Pat::Box { .. } => (), // not supported
+ }
+ }
+
+ fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
+ for expr in exprs {
+ self.consume_expr(expr);
+ }
+ }
+
+ fn closure_kind(&self) -> FnTrait {
+ let mut r = FnTrait::Fn;
+ for x in &self.current_captures {
+ r = cmp::min(
+ r,
+ match &x.kind {
+ CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
+ FnTrait::FnMut
+ }
+ CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
+ CaptureKind::ByValue => FnTrait::FnOnce,
+ },
+ )
+ }
+ r
+ }
+
+ fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
+ let (_, root) = self.db.lookup_intern_closure(closure.into());
+ self.current_closure = Some(closure);
+ let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
+ unreachable!("Closure expression id is always closure");
+ };
+ self.consume_expr(*body);
+ for item in &self.current_captures {
+ if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. }))
+ && !item.place.projections.contains(&ProjectionElem::Deref)
+ {
+ // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
+ // MIR. I didn't do that due duplicate diagnostics.
+ self.result.mutated_bindings_in_closure.insert(item.place.local);
+ }
+ }
+ // closure_kind should be done before adjust_for_move_closure
+ let closure_kind = self.closure_kind();
+ match capture_by {
+ CaptureBy::Value => self.adjust_for_move_closure(),
+ CaptureBy::Ref => (),
+ }
+ self.minimize_captures();
+ let result = mem::take(&mut self.current_captures);
+ let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
+ self.result.closure_info.insert(closure, (captures, closure_kind));
+ closure_kind
+ }
+
+ pub(crate) fn infer_closures(&mut self) {
+ let deferred_closures = self.sort_closures();
+ for (closure, exprs) in deferred_closures.into_iter().rev() {
+ self.current_captures = vec![];
+ let kind = self.analyze_closure(closure);
+
+ for (derefed_callee, callee_ty, params, expr) in exprs {
+ if let &Expr::Call { callee, .. } = &self.body[expr] {
+ let mut adjustments =
+ self.result.expr_adjustments.remove(&callee).unwrap_or_default();
+ self.write_fn_trait_method_resolution(
+ kind,
+ &derefed_callee,
+ &mut adjustments,
+ &callee_ty,
+ &params,
+ expr,
+ );
+ self.result.expr_adjustments.insert(callee, adjustments);
+ }
+ }
+ }
+ }
+
+ /// We want to analyze some closures before others, to have a correct analysis:
+ /// * We should analyze nested closures before the parent, since the parent should capture some of
+ /// the things that its children captures.
+ /// * If a closure calls another closure, we need to analyze the callee, to find out how we should
+ /// capture it (e.g. by move for FnOnce)
+ ///
+ /// These dependencies are collected in the main inference. We do a topological sort in this function. It
+ /// will consume the `deferred_closures` field and return its content in a sorted vector.
+ fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>)> {
+ let mut deferred_closures = mem::take(&mut self.deferred_closures);
+ let mut dependents_count: FxHashMap<ClosureId, usize> =
+ deferred_closures.keys().map(|x| (*x, 0)).collect();
+ for (_, deps) in &self.closure_dependencies {
+ for dep in deps {
+ *dependents_count.entry(*dep).or_default() += 1;
+ }
+ }
+ let mut queue: Vec<_> =
+ deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect();
+ let mut result = vec![];
+ while let Some(x) = queue.pop() {
+ if let Some(d) = deferred_closures.remove(&x) {
+ result.push((x, d));
+ }
+ for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) {
+ let cnt = dependents_count.get_mut(dep).unwrap();
+ *cnt -= 1;
+ if *cnt == 0 {
+ queue.push(*dep);
+ }
+ }
+ }
+ result
+ }
+}
+
+fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option<HirPlace> {
+ for adj in adjustments {
+ match &adj.kind {
+ Adjust::Deref(None) => {
+ r.projections.push(ProjectionElem::Deref);
+ }
+ _ => return None,
+ }
+ }
+ Some(r)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 48c915302..05a476f63 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -5,14 +5,15 @@
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
//! `rustc_hir_analysis/check/coercion.rs`.
-use std::{iter, sync::Arc};
+use std::iter;
-use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind};
use hir_def::{
- expr::ExprId,
+ hir::ExprId,
lang_item::{LangItem, LangItemTarget},
};
use stdx::always;
+use triomphe::Arc;
use crate::{
autoderef::{Autoderef, AutoderefKind},
@@ -21,8 +22,10 @@ use crate::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch,
},
- static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
- Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+ static_lifetime,
+ utils::ClosureSubst,
+ Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
+ Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
use super::unify::InferenceTable;
@@ -47,15 +50,23 @@ fn success(
Ok(InferOk { goals, value: (adj, target) })
}
+pub(super) enum CoercionCause {
+ // FIXME: Make better use of this. Right now things like return and break without a value
+ // use it to point to themselves, causing us to report a mismatch on those expressions even
+ // though technically they themselves are `!`
+ Expr(ExprId),
+}
+
#[derive(Clone, Debug)]
pub(super) struct CoerceMany {
expected_ty: Ty,
final_ty: Option<Ty>,
+ expressions: Vec<ExprId>,
}
impl CoerceMany {
pub(super) fn new(expected: Ty) -> Self {
- CoerceMany { expected_ty: expected, final_ty: None }
+ CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
}
/// Returns the "expected type" with which this coercion was
@@ -86,8 +97,12 @@ impl CoerceMany {
}
}
- pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
- self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
+ pub(super) fn coerce_forced_unit(
+ &mut self,
+ ctx: &mut InferenceContext<'_>,
+ cause: CoercionCause,
+ ) {
+ self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause)
}
/// Merge two types from different branches, with possible coercion.
@@ -102,6 +117,7 @@ impl CoerceMany {
ctx: &mut InferenceContext<'_>,
expr: Option<ExprId>,
expr_ty: &Ty,
+ cause: CoercionCause,
) {
let expr_ty = ctx.resolve_ty_shallow(expr_ty);
self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty);
@@ -110,6 +126,8 @@ impl CoerceMany {
// pointers to have a chance at getting a match. See
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
+ (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
+ (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
// we should be coercing the closure to a fn pointer of the safety of the FnDef
@@ -125,8 +143,15 @@ impl CoerceMany {
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) {
- ctx.table.register_infer_ok(result1);
- ctx.table.register_infer_ok(result2);
+ ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
+ for &e in &self.expressions {
+ ctx.write_expr_adj(e, result1.value.0.clone());
+ }
+ ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
+ if let Some(expr) = expr {
+ ctx.write_expr_adj(expr, result2.value.0);
+ self.expressions.push(expr);
+ }
return self.final_ty = Some(target_ty);
}
}
@@ -140,14 +165,19 @@ impl CoerceMany {
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
self.final_ty = Some(res);
} else {
- if let Some(id) = expr {
- ctx.result.type_mismatches.insert(
- id.into(),
- TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() },
- );
+ match cause {
+ CoercionCause::Expr(id) => {
+ ctx.result.type_mismatches.insert(
+ id.into(),
+ TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() },
+ );
+ }
}
cov_mark::hit!(coerce_merge_fail_fallback);
}
+ if let Some(expr) = expr {
+ self.expressions.push(expr);
+ }
}
}
@@ -625,7 +655,7 @@ impl<'a> InferenceTable<'a> {
// Need to find out in what cases this is necessary
let solution = self
.db
- .trait_solve(krate, canonicalized.value.clone().cast(Interner))
+ .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner))
.ok_or(TypeError)?;
match solution {
@@ -657,7 +687,7 @@ impl<'a> InferenceTable<'a> {
}
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
- let closure_sig = closure_substs.at(Interner, 0).assert_ty_ref(Interner).clone();
+ let closure_sig = ClosureSubst(closure_substs).sig_ty().clone();
match closure_sig.kind(Interner) {
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
num_binders: fn_ty.num_binders,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index ee186673e..194471f00 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -6,37 +6,43 @@ use std::{
};
use chalk_ir::{
- cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
+ cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
};
use hir_def::{
- expr::{
+ generics::TypeOrConstParamData,
+ hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
- generics::TypeOrConstParamData,
- lang_item::LangItem,
+ lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs},
- ConstParamId, FieldId, ItemContainerId, Lookup,
+ BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
};
use hir_expand::name::{name, Name};
use stdx::always;
use syntax::ast::RangeOp;
+use triomphe::Arc;
use crate::{
- autoderef::{self, Autoderef},
+ autoderef::{builtin_deref, deref_by_trait, Autoderef},
consteval,
infer::{
- coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
+ coerce::{CoerceMany, CoercionCause},
+ find_continuable,
+ pat::contains_explicit_ref_binding,
+ BreakableKind,
},
+ lang_items::lang_items_for_bin_op,
lower::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
},
mapping::{from_chalk, ToChalk},
- method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
+ method_resolution::{self, VisibleFromModule},
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
+ traits::FnTrait,
utils::{generics, Generics},
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
- Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
+ Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
@@ -83,10 +89,10 @@ impl<'a> InferenceContext<'a> {
}
}
- pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+ fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(expr, expected);
// While we don't allow *arbitrary* coercions here, we *do* allow
- // coercions from ! to `expected`.
+ // coercions from `!` to `expected`.
if ty.is_never() {
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
@@ -96,13 +102,22 @@ impl<'a> InferenceContext<'a> {
};
}
- let adj_ty = self.table.new_type_var();
- self.write_expr_adj(
- expr,
- vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
- );
- adj_ty
+ if let Some(target) = expected.only_has_type(&mut self.table) {
+ self.coerce(Some(expr), &ty, &target)
+ .expect("never-to-any coercion should always succeed")
+ } else {
+ ty
+ }
} else {
+ if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+ let could_unify = self.unify(&ty, &expected_ty);
+ if !could_unify {
+ self.result.type_mismatches.insert(
+ expr.into(),
+ TypeMismatch { expected: expected_ty, actual: ty.clone() },
+ );
+ }
+ }
ty
}
}
@@ -120,24 +135,28 @@ impl<'a> InferenceContext<'a> {
);
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let mut both_arms_diverge = Diverges::Always;
let then_ty = self.infer_expr_inner(then_branch, expected);
- both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
+ let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
- coerce.coerce(self, Some(then_branch), &then_ty);
+ coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr_inner(else_branch, expected);
- coerce.coerce(self, Some(else_branch), &else_ty);
+ let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ coerce.coerce(
+ self,
+ Some(else_branch),
+ &else_ty,
+ CoercionCause::Expr(else_branch),
+ );
+ self.diverges = condition_diverges | then_diverges & else_diverges;
}
None => {
- coerce.coerce_forced_unit(self);
+ coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr));
+ self.diverges = condition_diverges;
}
}
- both_arms_diverge &= self.diverges;
-
- self.diverges = condition_diverges | both_arms_diverge;
coerce.complete(self)
}
@@ -146,67 +165,21 @@ impl<'a> InferenceContext<'a> {
self.infer_top_pat(pat, &input_ty);
self.result.standard_types.bool_.clone()
}
- Expr::Block { statements, tail, label, id: _ } => {
- self.infer_block(tgt_expr, statements, *tail, *label, expected)
+ Expr::Block { statements, tail, label, id } => {
+ self.infer_block(tgt_expr, *id, statements, *tail, *label, expected)
}
- Expr::Unsafe { id: _, statements, tail } => {
- self.infer_block(tgt_expr, statements, *tail, None, expected)
+ Expr::Unsafe { id, statements, tail } => {
+ self.infer_block(tgt_expr, *id, statements, *tail, None, expected)
}
- Expr::Const { id: _, statements, tail } => {
+ Expr::Const(id) => {
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- this.infer_block(tgt_expr, statements, *tail, None, expected)
+ let loc = this.db.lookup_intern_anonymous_const(*id);
+ this.infer_expr(loc.root, expected)
})
.1
}
- Expr::TryBlock { id: _, statements, tail } => {
- // The type that is returned from the try block
- let try_ty = self.table.new_type_var();
- if let Some(ty) = expected.only_has_type(&mut self.table) {
- self.unify(&try_ty, &ty);
- }
-
- // The ok-ish type that is expected from the last expression
- let ok_ty =
- self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
-
- self.infer_block(
- tgt_expr,
- statements,
- *tail,
- None,
- &Expectation::has_type(ok_ty.clone()),
- );
- try_ty
- }
- Expr::Async { id: _, statements, tail } => {
- let ret_ty = self.table.new_type_var();
- let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
- let prev_ret_coercion =
- mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
-
- let (_, inner_ty) =
- self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- this.infer_block(
- tgt_expr,
- statements,
- *tail,
- None,
- &Expectation::has_type(ret_ty),
- )
- });
-
- self.diverges = prev_diverges;
- self.return_ty = prev_ret_ty;
- self.return_coercion = prev_ret_coercion;
-
- // Use the first type parameter as the output type of future.
- // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
- let impl_trait_id =
- crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
- let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
- TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
- .intern(Interner)
+ Expr::Async { id, statements, tail } => {
+ self.infer_async_block(tgt_expr, id, statements, tail)
}
&Expr::Loop { body, label } => {
// FIXME: should be:
@@ -238,25 +211,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
- &Expr::For { iterable, body, pat, label } => {
- let iterable_ty = self.infer_expr(iterable, &Expectation::none());
- let into_iter_ty =
- self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
- let pat_ty = self
- .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item());
-
- self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty);
-
- self.infer_top_pat(pat, &pat_ty);
- self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
- this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
- });
-
- // the body may not run, so it diverging doesn't mean we diverge
- self.diverges = Diverges::Maybe;
- TyBuilder::unit()
- }
- Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
+ Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
@@ -276,18 +231,7 @@ impl<'a> InferenceContext<'a> {
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
- // Use the first type parameter as the output type of future.
- // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
- let impl_trait_id =
- crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
- let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
- sig_tys.push(
- TyKind::OpaqueType(
- opaque_ty_id,
- Substitution::from1(Interner, ret_ty.clone()),
- )
- .intern(Interner),
- );
+ sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body));
} else {
sig_tys.push(ret_ty.clone());
}
@@ -302,7 +246,7 @@ impl<'a> InferenceContext<'a> {
})
.intern(Interner);
- let (ty, resume_yield_tys) = match closure_kind {
+ let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Generator(_) => {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
@@ -322,17 +266,20 @@ impl<'a> InferenceContext<'a> {
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
- (generator_ty, Some((resume_ty, yield_ty)))
+ (None, generator_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure(
closure_id,
- Substitution::from1(Interner, sig_ty.clone()),
+ TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
)
.intern(Interner);
-
- (closure_ty, None)
+ self.deferred_closures.entry(closure_id).or_default();
+ if let Some(c) = self.current_closure {
+ self.closure_dependencies.entry(c).or_default().push(closure_id);
+ }
+ (Some(closure_id), closure_ty, None)
}
};
@@ -348,9 +295,10 @@ impl<'a> InferenceContext<'a> {
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
let prev_ret_coercion =
- mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
+ mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
let prev_resume_yield_tys =
mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
@@ -361,6 +309,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
+ self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
ty
@@ -385,16 +334,31 @@ impl<'a> InferenceContext<'a> {
|| res.is_none();
let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => {
- let adjustments = auto_deref_adjust_steps(&derefs);
- // FIXME: Handle call adjustments for Fn/FnMut
- self.write_expr_adj(*callee, adjustments);
- if let Some((trait_, func)) = func {
- let subst = TyBuilder::subst_for_def(self.db, trait_, None)
- .push(callee_ty.clone())
- .push(TyBuilder::tuple_with(params.iter().cloned()))
- .build();
- self.write_method_resolution(tgt_expr, func, subst.clone());
+ let mut adjustments = auto_deref_adjust_steps(&derefs);
+ if let TyKind::Closure(c, _) =
+ self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+ {
+ if let Some(par) = self.current_closure {
+ self.closure_dependencies.entry(par).or_default().push(*c);
+ }
+ self.deferred_closures.entry(*c).or_default().push((
+ derefed_callee.clone(),
+ callee_ty.clone(),
+ params.clone(),
+ tgt_expr,
+ ));
}
+ if let Some(fn_x) = func {
+ self.write_fn_trait_method_resolution(
+ fn_x,
+ &derefed_callee,
+ &mut adjustments,
+ &callee_ty,
+ &params,
+ tgt_expr,
+ );
+ }
+ self.write_expr_adj(*callee, adjustments);
(params, ret_ty)
}
None => {
@@ -470,7 +434,7 @@ impl<'a> InferenceContext<'a> {
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
all_arms_diverge &= self.diverges;
- coerce.coerce(self, Some(arm.expr), &arm_ty);
+ coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
}
self.diverges = matchee_diverges | all_arms_diverge;
@@ -484,8 +448,8 @@ impl<'a> InferenceContext<'a> {
self.resolver.reset_to_guard(g);
ty
}
- Expr::Continue { label } => {
- if let None = find_continuable(&mut self.breakables, label.as_ref()) {
+ &Expr::Continue { label } => {
+ if let None = find_continuable(&mut self.breakables, label) {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
is_break: false,
@@ -494,9 +458,9 @@ impl<'a> InferenceContext<'a> {
};
self.result.standard_types.never.clone()
}
- Expr::Break { expr, label } => {
- let val_ty = if let Some(expr) = *expr {
- let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
+ &Expr::Break { expr, label } => {
+ let val_ty = if let Some(expr) = expr {
+ let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce {
Some(coerce) => coerce.expected_ty(),
None => {
@@ -515,13 +479,17 @@ impl<'a> InferenceContext<'a> {
TyBuilder::unit()
};
- match find_breakable(&mut self.breakables, label.as_ref()) {
+ match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
- coerce.coerce(self, *expr, &val_ty);
+ let cause = match expr {
+ Some(expr) => CoercionCause::Expr(expr),
+ None => CoercionCause::Expr(tgt_expr),
+ };
+ coerce.coerce(self, expr, &val_ty, cause);
// Avoiding borrowck
- let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+ let ctxt = find_breakable(&mut self.breakables, label)
.expect("breakable stack changed during coercion");
ctxt.may_break = true;
ctxt.coerce = Some(coerce);
@@ -538,7 +506,7 @@ impl<'a> InferenceContext<'a> {
}
self.result.standard_types.never.clone()
}
- &Expr::Return { expr } => self.infer_expr_return(expr),
+ &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr),
Expr::Yield { expr } => {
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
if let Some(expr) = expr {
@@ -589,6 +557,9 @@ impl<'a> InferenceContext<'a> {
let field_ty = field_def.map_or(self.err_ty(), |it| {
field_types[it.local_id].clone().substitute(Interner, &substs)
});
+ // Field type might have some unknown types
+ // FIXME: we may want to emit a single type variable for all instance of type fields?
+ let field_ty = self.insert_type_vars(field_ty);
self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
}
if let Some(expr) = spread {
@@ -601,26 +572,18 @@ impl<'a> InferenceContext<'a> {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
}
- Expr::Try { expr } => {
- let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
- if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
- if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
- let subst = TyBuilder::subst_for_def(self.db, trait_, None)
- .push(inner_ty.clone())
- .build();
- self.write_method_resolution(tgt_expr, func, subst.clone());
- }
- let try_output = self.resolve_output_on(trait_);
- self.resolve_associated_type(inner_ty, try_output)
- } else {
- self.err_ty()
- }
- }
Expr::Cast { expr, type_ref } => {
let cast_ty = self.make_ty(type_ref);
// FIXME: propagate the "castable to" expectation
- let _inner_ty = self.infer_expr_no_expect(*expr);
- // FIXME check the cast...
+ let inner_ty = self.infer_expr_no_expect(*expr);
+ match (inner_ty.kind(Interner), cast_ty.kind(Interner)) {
+ (TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => {
+ // FIXME: record invalid cast diagnostic in case of mismatch
+ self.unify(inner, cast);
+ }
+ // FIXME check the other kinds of cast...
+ _ => (),
+ }
cast_ty
}
Expr::Ref { expr, rawness, mutability } => {
@@ -638,7 +601,7 @@ impl<'a> InferenceContext<'a> {
// FIXME: record type error - expected reference but found ptr,
// which cannot be coerced
}
- Expectation::rvalue_hint(&mut self.table, Ty::clone(exp_inner))
+ Expectation::rvalue_hint(self, Ty::clone(exp_inner))
} else {
Expectation::none()
};
@@ -656,7 +619,25 @@ impl<'a> InferenceContext<'a> {
// FIXME: Note down method resolution her
match op {
UnaryOp::Deref => {
- autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
+ if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref])
+ {
+ // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
+ // the mutability is not wrong, and will be fixed in `self.infer_mut`).
+ self.write_method_resolution(
+ tgt_expr,
+ deref_fn,
+ Substitution::empty(Interner),
+ );
+ }
+ }
+ if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) {
+ self.resolve_ty_shallow(derefed)
+ } else {
+ deref_by_trait(&mut self.table, inner_ty)
+ .unwrap_or_else(|| self.err_ty())
+ }
}
UnaryOp::Neg => {
match inner_ty.kind(Interner) {
@@ -767,14 +748,16 @@ impl<'a> InferenceContext<'a> {
let canonicalized = self.canonicalize(base_ty.clone());
let receiver_adjustments = method_resolution::resolve_indexing_op(
self.db,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
canonicalized.value,
index_trait,
);
- let (self_ty, adj) = receiver_adjustments
+ let (self_ty, mut adj) = receiver_adjustments
.map_or((self.err_ty(), Vec::new()), |adj| {
adj.apply(&mut self.table, base_ty)
});
+ // mutability will be fixed up in `InferenceContext::infer_mut`;
+ adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone()));
self.write_expr_adj(*base, adj);
if let Some(func) =
self.db.trait_data(index_trait).method_by_name(&name!(index))
@@ -783,7 +766,7 @@ impl<'a> InferenceContext<'a> {
.push(self_ty.clone())
.push(index_ty.clone())
.build();
- self.write_method_resolution(tgt_expr, func, substs.clone());
+ self.write_method_resolution(tgt_expr, func, substs);
}
self.resolve_associated_type_with_params(
self_ty,
@@ -834,6 +817,20 @@ impl<'a> InferenceContext<'a> {
let array_type = TyKind::Array(byte_type, len).intern(Interner);
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
}
+ Literal::CString(..) => TyKind::Ref(
+ Mutability::Not,
+ static_lifetime(),
+ self.resolve_lang_item(LangItem::CStr)
+ .and_then(LangItemTarget::as_struct)
+ .map_or_else(
+ || self.err_ty(),
+ |strukt| {
+ TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner))
+ .intern(Interner)
+ },
+ ),
+ )
+ .intern(Interner),
Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner),
Literal::Int(_v, ty) => match ty {
Some(int_ty) => {
@@ -859,9 +856,15 @@ impl<'a> InferenceContext<'a> {
},
Expr::Underscore => {
// Underscore expressions may only appear in assignee expressions,
- // which are handled by `infer_assignee_expr()`, so any underscore
- // expression reaching this branch is an error.
- self.err_ty()
+ // which are handled by `infer_assignee_expr()`.
+ // Any other underscore expression is an error, we render a specialized diagnostic
+ // to let the user know what type is expected though.
+ let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
+ self.push_diagnostic(InferenceDiagnostic::TypedHole {
+ expr: tgt_expr,
+ expected: expected.clone(),
+ });
+ expected
}
};
// use a new type variable if we got unknown here
@@ -874,6 +877,88 @@ impl<'a> InferenceContext<'a> {
ty
}
+ fn infer_async_block(
+ &mut self,
+ tgt_expr: ExprId,
+ id: &Option<BlockId>,
+ statements: &[Statement],
+ tail: &Option<ExprId>,
+ ) -> Ty {
+ let ret_ty = self.table.new_type_var();
+ let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+ let prev_ret_coercion =
+ mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
+
+ let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+ this.infer_block(tgt_expr, *id, statements, *tail, None, &Expectation::has_type(ret_ty))
+ });
+
+ self.diverges = prev_diverges;
+ self.return_ty = prev_ret_ty;
+ self.return_coercion = prev_ret_coercion;
+
+ self.lower_async_block_type_impl_trait(inner_ty, tgt_expr)
+ }
+
+ pub(crate) fn lower_async_block_type_impl_trait(
+ &mut self,
+ inner_ty: Ty,
+ tgt_expr: ExprId,
+ ) -> Ty {
+ // Use the first type parameter as the output type of future.
+ // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
+ let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
+ let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
+ TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner)
+ }
+
+ pub(crate) fn write_fn_trait_method_resolution(
+ &mut self,
+ fn_x: FnTrait,
+ derefed_callee: &Ty,
+ adjustments: &mut Vec<Adjustment>,
+ callee_ty: &Ty,
+ params: &Vec<Ty>,
+ tgt_expr: ExprId,
+ ) {
+ match fn_x {
+ FnTrait::FnOnce => (),
+ FnTrait::FnMut => {
+ if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
+ if adjustments
+ .last()
+ .map(|x| matches!(x.kind, Adjust::Borrow(_)))
+ .unwrap_or(true)
+ {
+ // prefer reborrow to move
+ adjustments
+ .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() });
+ adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone()))
+ }
+ } else {
+ adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
+ }
+ }
+ FnTrait::Fn => {
+ if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
+ adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
+ }
+ }
+ }
+ let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else {
+ return;
+ };
+ let trait_data = self.db.trait_data(trait_);
+ if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+ .push(callee_ty.clone())
+ .push(TyBuilder::tuple_with(params.iter().cloned()))
+ .build();
+ self.write_method_resolution(tgt_expr, func, subst.clone());
+ }
+ }
+
fn infer_expr_array(
&mut self,
array: &Array,
@@ -892,10 +977,10 @@ impl<'a> InferenceContext<'a> {
(elem_ty, consteval::usize_const(self.db, Some(0), krate))
}
Array::ElementList { elements, .. } => {
- let mut coerce = CoerceMany::new(elem_ty.clone());
+ let mut coerce = CoerceMany::new(elem_ty);
for &expr in elements.iter() {
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
- coerce.coerce(self, Some(expr), &cur_elem_ty);
+ coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
}
(
coerce.complete(self),
@@ -904,12 +989,13 @@ impl<'a> InferenceContext<'a> {
}
&Array::Repeat { initializer, repeat } => {
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
- self.infer_expr(
- repeat,
- &Expectation::HasType(
- TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
- ),
- );
+ let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
+ match self.body[repeat] {
+ Expr::Underscore => {
+ self.write_expr_ty(repeat, usize);
+ }
+ _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)),
+ }
(
elem_ty,
@@ -928,7 +1014,8 @@ impl<'a> InferenceContext<'a> {
)
}
};
-
+ // Try to evaluate unevaluated constant, and insert variable if is not possible.
+ let len = self.table.insert_const_vars_shallow(len);
TyKind::Array(elem_ty, len).intern(Interner)
}
@@ -940,18 +1027,18 @@ impl<'a> InferenceContext<'a> {
.expected_ty();
let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
let mut coerce_many = self.return_coercion.take().unwrap();
- coerce_many.coerce(self, Some(expr), &return_expr_ty);
+ coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
self.return_coercion = Some(coerce_many);
}
- fn infer_expr_return(&mut self, expr: Option<ExprId>) -> Ty {
+ fn infer_expr_return(&mut self, ret: ExprId, expr: Option<ExprId>) -> Ty {
match self.return_coercion {
Some(_) => {
if let Some(expr) = expr {
self.infer_return(expr);
} else {
let mut coerce = self.return_coercion.take().unwrap();
- coerce.coerce_forced_unit(self);
+ coerce.coerce_forced_unit(self, CoercionCause::Expr(ret));
self.return_coercion = Some(coerce);
}
}
@@ -976,7 +1063,7 @@ impl<'a> InferenceContext<'a> {
.filter(|(e_adt, _)| e_adt == &box_id)
.map(|(_, subts)| {
let g = subts.at(Interner, 0);
- Expectation::rvalue_hint(table, Ty::clone(g.assert_ty_ref(Interner)))
+ Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner)))
})
.unwrap_or_else(Expectation::none);
@@ -1185,6 +1272,7 @@ impl<'a> InferenceContext<'a> {
fn infer_block(
&mut self,
expr: ExprId,
+ block_id: Option<BlockId>,
statements: &[Statement],
tail: Option<ExprId>,
label: Option<LabelId>,
@@ -1192,9 +1280,14 @@ impl<'a> InferenceContext<'a> {
) -> Ty {
let coerce_ty = expected.coercion_target_type(&mut self.table);
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
+ let prev_env = block_id.map(|block_id| {
+ let prev_env = self.table.trait_env.clone();
+ Arc::make_mut(&mut self.table.trait_env).block = Some(block_id);
+ prev_env
+ });
let (break_ty, ty) =
- self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
+ self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| {
for stmt in statements {
match stmt {
Statement::Let { pat, type_ref, initializer, else_branch } => {
@@ -1280,6 +1373,9 @@ impl<'a> InferenceContext<'a> {
}
});
self.resolver.reset_to_guard(g);
+ if let Some(prev_env) = prev_env {
+ self.table.trait_env = prev_env;
+ }
break_ty.unwrap_or(ty)
}
@@ -1378,7 +1474,7 @@ impl<'a> InferenceContext<'a> {
method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
name,
@@ -1411,7 +1507,7 @@ impl<'a> InferenceContext<'a> {
let resolved = method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
method_name,
@@ -1562,7 +1658,7 @@ impl<'a> InferenceContext<'a> {
// the parameter to coerce to the expected type (for example in
// `coerce_unsize_expected_type_4`).
let param_ty = self.normalize_associated_types_in(param_ty);
- let expected = Expectation::rvalue_hint(&mut self.table, expected_ty);
+ let expected = Expectation::rvalue_hint(self, expected_ty);
// infer with the expected type we have...
let ty = self.infer_expr_inner(arg, &expected);
@@ -1575,9 +1671,10 @@ impl<'a> InferenceContext<'a> {
} else {
param_ty
};
- if !coercion_target.is_unknown()
- && self.coerce(Some(arg), &ty, &coercion_target).is_err()
- {
+ // The function signature may contain some unknown types, so we need to insert
+ // type vars here to avoid type mismatch false positive.
+ let coercion_target = self.insert_type_vars(coercion_target);
+ if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
self.result.type_mismatches.insert(
arg.into(),
TypeMismatch { expected: coercion_target, actual: ty.clone() },
@@ -1618,6 +1715,7 @@ impl<'a> InferenceContext<'a> {
const_or_path_to_chalk(
this.db,
&this.resolver,
+ this.owner.into(),
ty,
c,
ParamLoweringMode::Placeholder,
@@ -1868,7 +1966,6 @@ impl<'a> InferenceContext<'a> {
cb: impl FnOnce(&mut Self) -> T,
) -> (Option<Ty>, T) {
self.breakables.push({
- let label = label.map(|label| self.body[label].name.clone());
BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
});
let res = cb(self);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
new file mode 100644
index 000000000..46f2e1d7d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -0,0 +1,218 @@
+//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
+//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
+
+use chalk_ir::Mutability;
+use hir_def::{
+ hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
+ lang_item::LangItem,
+};
+use hir_expand::name;
+
+use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref};
+
+use super::InferenceContext;
+
+impl<'a> InferenceContext<'a> {
+ pub(crate) fn infer_mut_body(&mut self) {
+ self.infer_mut_expr(self.body.body_expr, Mutability::Not);
+ }
+
+ fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
+ if let Some(adjustments) = self.result.expr_adjustments.get_mut(&tgt_expr) {
+ for adj in adjustments.iter_mut().rev() {
+ match &mut adj.kind {
+ Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
+ Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
+ Adjust::Borrow(b) => match b {
+ AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
+ },
+ }
+ }
+ }
+ self.infer_mut_expr_without_adjust(tgt_expr, mutability);
+ }
+
+ fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
+ match &self.body[tgt_expr] {
+ Expr::Missing => (),
+ &Expr::If { condition, then_branch, else_branch } => {
+ self.infer_mut_expr(condition, Mutability::Not);
+ self.infer_mut_expr(then_branch, Mutability::Not);
+ if let Some(else_branch) = else_branch {
+ self.infer_mut_expr(else_branch, Mutability::Not);
+ }
+ }
+ Expr::Const(id) => {
+ let loc = self.db.lookup_intern_anonymous_const(*id);
+ self.infer_mut_expr(loc.root, Mutability::Not);
+ }
+ Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
+ Expr::Block { id: _, statements, tail, label: _ }
+ | Expr::Async { id: _, statements, tail }
+ | Expr::Unsafe { id: _, statements, tail } => {
+ for st in statements.iter() {
+ match st {
+ Statement::Let { pat, type_ref: _, initializer, else_branch } => {
+ if let Some(i) = initializer {
+ self.infer_mut_expr(*i, self.pat_bound_mutability(*pat));
+ }
+ if let Some(e) = else_branch {
+ self.infer_mut_expr(*e, Mutability::Not);
+ }
+ }
+ Statement::Expr { expr, has_semi: _ } => {
+ self.infer_mut_expr(*expr, Mutability::Not);
+ }
+ }
+ }
+ if let Some(tail) = tail {
+ self.infer_mut_expr(*tail, Mutability::Not);
+ }
+ }
+ &Expr::While { condition: c, body, label: _ } => {
+ self.infer_mut_expr(c, Mutability::Not);
+ self.infer_mut_expr(body, Mutability::Not);
+ }
+ Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ }
+ | Expr::Call { callee: x, args, is_assignee_expr: _ } => {
+ self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x)));
+ }
+ Expr::Match { expr, arms } => {
+ let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat));
+ self.infer_mut_expr(*expr, m);
+ for arm in arms.iter() {
+ self.infer_mut_expr(arm.expr, Mutability::Not);
+ if let Some(g) = arm.guard {
+ self.infer_mut_expr(g, Mutability::Not);
+ }
+ }
+ }
+ Expr::Yield { expr }
+ | Expr::Yeet { expr }
+ | Expr::Return { expr }
+ | Expr::Break { expr, label: _ } => {
+ if let &Some(expr) = expr {
+ self.infer_mut_expr(expr, Mutability::Not);
+ }
+ }
+ Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
+ self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
+ }
+ &Expr::Index { base, index } => {
+ if mutability == Mutability::Mut {
+ if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
+ if let Some(index_trait) = self
+ .db
+ .lang_item(self.table.trait_env.krate, LangItem::IndexMut)
+ .and_then(|l| l.as_trait())
+ {
+ if let Some(index_fn) =
+ self.db.trait_data(index_trait).method_by_name(&name![index_mut])
+ {
+ *f = index_fn;
+ let base_adjustments = self
+ .result
+ .expr_adjustments
+ .get_mut(&base)
+ .and_then(|it| it.last_mut());
+ if let Some(Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
+ ..
+ }) = base_adjustments
+ {
+ *mutability = Mutability::Mut;
+ }
+ }
+ }
+ }
+ }
+ self.infer_mut_expr(base, mutability);
+ self.infer_mut_expr(index, Mutability::Not);
+ }
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
+ if mutability == Mutability::Mut {
+ if let Some(deref_trait) = self
+ .db
+ .lang_item(self.table.trait_env.krate, LangItem::DerefMut)
+ .and_then(|l| l.as_trait())
+ {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
+ {
+ *f = deref_fn;
+ }
+ }
+ }
+ }
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::Field { expr, name: _ } => {
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::UnaryOp { expr, op: _ }
+ | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ }
+ | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ }
+ | Expr::Await { expr }
+ | Expr::Box { expr }
+ | Expr::Loop { body: expr, label: _ }
+ | Expr::Cast { expr, type_ref: _ } => {
+ self.infer_mut_expr(*expr, Mutability::Not);
+ }
+ Expr::Ref { expr, rawness: _, mutability } => {
+ let mutability = lower_to_chalk_mutability(*mutability);
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
+ self.infer_mut_expr(*lhs, Mutability::Mut);
+ self.infer_mut_expr(*rhs, Mutability::Not);
+ }
+ Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
+ | Expr::BinaryOp { lhs, rhs, op: _ }
+ | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
+ self.infer_mut_expr(*lhs, Mutability::Not);
+ self.infer_mut_expr(*rhs, Mutability::Not);
+ }
+ Expr::Closure { body, .. } => {
+ self.infer_mut_expr(*body, Mutability::Not);
+ }
+ Expr::Tuple { exprs, is_assignee_expr: _ }
+ | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
+ self.infer_mut_not_expr_iter(exprs.iter().copied());
+ }
+ // These don't need any action, as they don't have sub expressions
+ Expr::Range { lhs: None, rhs: None, range_type: _ }
+ | Expr::Literal(_)
+ | Expr::Path(_)
+ | Expr::Continue { .. }
+ | Expr::Underscore => (),
+ }
+ }
+
+ fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator<Item = ExprId>) {
+ for expr in exprs {
+ self.infer_mut_expr(expr, Mutability::Not);
+ }
+ }
+
+ fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mutability {
+ if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) {
+ Mutability::Mut
+ } else {
+ Mutability::Not
+ }
+ }
+
+ /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
+ /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in
+ /// `let (ref x0, ref x1) = *x;` we should use `Deref`.
+ fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
+ let mut r = Mutability::Not;
+ self.body.walk_bindings_in_pat(pat, |b| {
+ if self.body.bindings[b].mode == BindingAnnotation::RefMut {
+ r = Mutability::Mut;
+ }
+ });
+ r
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 5f839fc30..2480f8bab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -5,7 +5,7 @@ use std::iter::repeat_with;
use chalk_ir::Mutability;
use hir_def::{
body::Body,
- expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
+ hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
path::Path,
};
use hir_expand::name::Name;
@@ -255,15 +255,15 @@ impl<'a> InferenceContext<'a> {
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
}
Pat::Wild => expected.clone(),
- Pat::Range { start, end } => {
- let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
- self.infer_expr(*end, &Expectation::has_type(start_ty))
+ Pat::Range { .. } => {
+ // FIXME: do some checks here.
+ expected.clone()
}
&Pat::Lit(expr) => {
// Don't emit type mismatches again, the expression lowering already did that.
let ty = self.infer_lit_pat(expr, &expected);
self.write_pat_ty(pat, ty.clone());
- return ty;
+ return self.pat_ty_after_adjustment(pat);
}
Pat::Box { inner } => match self.resolve_boxed_box() {
Some(box_adt) => {
@@ -298,22 +298,38 @@ impl<'a> InferenceContext<'a> {
.type_mismatches
.insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
}
- self.write_pat_ty(pat, ty.clone());
- ty
+ self.write_pat_ty(pat, ty);
+ self.pat_ty_after_adjustment(pat)
+ }
+
+ fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty {
+ self.result
+ .pat_adjustments
+ .get(&pat)
+ .and_then(|x| x.first())
+ .unwrap_or(&self.result.type_of_pat[pat])
+ .clone()
}
fn infer_ref_pat(
&mut self,
- pat: PatId,
+ inner_pat: PatId,
mutability: Mutability,
expected: &Ty,
default_bm: BindingMode,
) -> Ty {
let expectation = match expected.as_reference() {
Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
- _ => self.result.standard_types.unknown.clone(),
+ None => {
+ let inner_ty = self.table.new_type_var();
+ let ref_ty =
+ TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner);
+ // Unification failure will be reported by the caller.
+ self.unify(&ref_ty, expected);
+ inner_ty
+ }
};
- let subty = self.infer_pat(pat, &expectation, default_bm);
+ let subty = self.infer_pat(inner_pat, &expectation, default_bm);
TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
}
@@ -331,7 +347,7 @@ impl<'a> InferenceContext<'a> {
} else {
BindingMode::convert(mode)
};
- self.result.pat_binding_modes.insert(pat, mode);
+ self.result.binding_modes.insert(binding, mode);
let inner_ty = match subpat {
Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
@@ -345,7 +361,7 @@ impl<'a> InferenceContext<'a> {
}
BindingMode::Move => inner_ty.clone(),
};
- self.write_pat_ty(pat, bound_ty.clone());
+ self.write_pat_ty(pat, inner_ty.clone());
self.write_binding_ty(binding, bound_ty);
return inner_ty;
}
@@ -370,7 +386,7 @@ impl<'a> InferenceContext<'a> {
if let &Some(slice_pat_id) = slice {
let rest_pat_ty = match expected.kind(Interner) {
TyKind::Array(_, length) => {
- let len = try_const_usize(length);
+ let len = try_const_usize(self.db, length);
let len =
len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128));
TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate()))
@@ -419,17 +435,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
// FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
Pat::Path(..) => true,
Pat::ConstBlock(..) => true,
- Pat::Lit(expr) => {
- !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
- }
- Pat::Bind { id, subpat: Some(subpat), .. }
- if matches!(
- body.bindings[*id].mode,
- BindingAnnotation::Mutable | BindingAnnotation::Unannotated
- ) =>
- {
- is_non_ref_pat(body, *subpat)
- }
+ Pat::Lit(expr) => !matches!(
+ body[*expr],
+ Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
+ ),
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
}
}
@@ -437,7 +446,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
let mut res = false;
body.walk_pats(pat_id, &mut |pat| {
- res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
+ res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
});
res
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 2267fedaa..79d9e21e7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -4,7 +4,7 @@ use chalk_ir::cast::Cast;
use hir_def::{
path::{Path, PathSegment},
resolver::{ResolveValueResult, TypeNs, ValueNs},
- AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
+ AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup,
};
use hir_expand::name::Name;
use stdx::never;
@@ -13,6 +13,7 @@ use crate::{
builder::ParamKind,
consteval,
method_resolution::{self, VisibleFromModule},
+ to_chalk_trait_id,
utils::generics,
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
ValueTyDefId,
@@ -20,26 +21,44 @@ use crate::{
use super::{ExprOrPatId, InferenceContext, TraitRef};
-impl<'a> InferenceContext<'a> {
+impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
- let ty = self.resolve_value_path(path, id)?;
- let ty = self.insert_type_vars(ty);
+ let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
+ ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
+ (value_def, generic_def, substs)
+ }
+ ValuePathResolution::NonGeneric(ty) => return Some(ty),
+ };
+ let substs = self.insert_type_vars(substs);
+ let substs = self.normalize_associated_types_in(substs);
+
+ self.add_required_obligations_for_value_path(generic_def, &substs);
+
+ let ty = self.db.value_ty(value_def).substitute(Interner, &substs);
let ty = self.normalize_associated_types_in(ty);
Some(ty)
}
- fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
+ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
- let Some(last) = path.segments().last() else { return None };
- let ty = self.make_ty(type_ref);
+ let last = path.segments().last()?;
+
+ // Don't use `self.make_ty()` here as we need `orig_ns`.
+ let ctx =
+ crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
+
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
+ let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
// FIXME: report error, unresolved first path segment
let value_or_partial =
- self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
+ self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
@@ -49,9 +68,9 @@ impl<'a> InferenceContext<'a> {
}
};
- let typable: ValueTyDefId = match value {
+ let value_def = match value {
ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
- Some(ty) => return Some(ty.clone()),
+ Some(ty) => return Some(ValuePathResolution::NonGeneric(ty.clone())),
None => {
never!("uninferred pattern?");
return None;
@@ -75,28 +94,45 @@ impl<'a> InferenceContext<'a> {
let substs = generics.placeholder_subst(self.db);
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
- let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
- return Some(ty);
+ return Some(ValuePathResolution::GenericDef(
+ struct_id.into(),
+ struct_id.into(),
+ substs.clone(),
+ ));
} else {
// FIXME: report error, invalid Self reference
return None;
}
}
- ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
+ ValueNs::GenericParam(it) => {
+ return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it)))
+ }
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- let substs = ctx.substs_from_path(path, typable, true);
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let substs = ctx.substs_from_path(path, value_def, true);
let substs = substs.as_slice(Interner);
let parent_substs = self_subst.or_else(|| {
- let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
+ let generics = generics(self.db.upcast(), value_def.to_generic_def_id()?);
let parent_params_len = generics.parent_generics()?.len();
let parent_args = &substs[substs.len() - parent_params_len..];
Some(Substitution::from_iter(Interner, parent_args))
});
let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
- let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
+
+ let Some(generic_def) = value_def.to_generic_def_id() else {
+ // `value_def` is the kind of item that can never be generic (i.e. statics, at least
+ // currently). We can just skip the binders to get its type.
+ let (ty, binders) = self.db.value_ty(value_def).into_value_and_skipped_binders();
+ stdx::always!(
+ parent_substs.is_none() && binders.is_empty(Interner),
+ "non-empty binders for non-generic def",
+ );
+ return Some(ValuePathResolution::NonGeneric(ty));
+ };
+ let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs);
+ let substs = builder
.fill(|x| {
it.next().unwrap_or_else(|| match x {
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
@@ -104,7 +140,35 @@ impl<'a> InferenceContext<'a> {
})
})
.build();
- Some(ty)
+
+ Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
+ }
+
+ fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
+ let predicates = self.db.generic_predicates(def);
+ for predicate in predicates.iter() {
+ let (predicate, binders) =
+ predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders();
+ // Quantified where clauses are not yet handled.
+ stdx::always!(binders.is_empty(Interner));
+ self.push_obligation(predicate.cast(Interner));
+ }
+
+ // We need to add `Self: Trait` obligation when `def` is a trait assoc item.
+ let container = match def {
+ GenericDefId::FunctionId(id) => id.lookup(self.db.upcast()).container,
+ GenericDefId::ConstId(id) => id.lookup(self.db.upcast()).container,
+ _ => return,
+ };
+
+ if let ItemContainerId::TraitId(trait_) = container {
+ let param_len = generics(self.db.upcast(), def).len_self();
+ let parent_subst =
+ Substitution::from_iter(Interner, subst.iter(Interner).skip(param_len));
+ let trait_ref =
+ TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst };
+ self.push_obligation(trait_ref.cast(Interner));
+ }
}
fn resolve_assoc_item(
@@ -127,7 +191,11 @@ impl<'a> InferenceContext<'a> {
(TypeNs::TraitId(trait_), true) => {
let segment =
remaining_segments.last().expect("there should be at least one segment here");
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ self.owner.into(),
+ );
let trait_ref =
ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
self.resolve_trait_assoc_item(trait_ref, segment, id)
@@ -139,7 +207,11 @@ impl<'a> InferenceContext<'a> {
// as Iterator>::Item::default`)
let remaining_segments_for_ty =
remaining_segments.take(remaining_segments.len() - 1);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ self.owner.into(),
+ );
let (ty, _) = ctx.lower_partly_resolved_path(
def,
resolved_segment,
@@ -169,7 +241,7 @@ impl<'a> InferenceContext<'a> {
) -> Option<(ValueNs, Substitution)> {
let trait_ = trait_ref.hir_trait_id();
let item =
- self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| {
+ self.db.trait_data(trait_).items.iter().map(|(_name, id)| *id).find_map(|item| {
match item {
AssocItemId::FunctionId(func) => {
if segment.name == &self.db.function_data(func).name {
@@ -288,7 +360,7 @@ impl<'a> InferenceContext<'a> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- let ty = self.resolve_ty_shallow(ty);
+ let ty = self.resolve_ty_shallow(&ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
@@ -300,3 +372,10 @@ impl<'a> InferenceContext<'a> {
Some((ValueNs::EnumVariantId(variant), subst.clone()))
}
}
+
+enum ValuePathResolution {
+ // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible
+ // conversion between them + `unwrap()`.
+ GenericDef(ValueTyDefId, GenericDefId, Substitution),
+ NonGeneric(Ty),
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 504f0743a..e33d8f179 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -1,23 +1,25 @@
//! Unification and canonicalization logic.
-use std::{fmt, iter, mem, sync::Arc};
+use std::{fmt, iter, mem};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
IntTy, TyVariableKind, UniverseIndex,
};
use chalk_solve::infer::ParameterEnaVariableExt;
+use either::Either;
use ena::unify::UnifyKey;
-use hir_def::{FunctionId, TraitId};
use hir_expand::name;
use stdx::never;
+use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
- db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
- Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
- InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
- Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+ consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime,
+ to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
+ DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar,
+ Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@@ -130,7 +132,7 @@ pub(crate) fn unify(
}
bitflags::bitflags! {
- #[derive(Default)]
+ #[derive(Default, Clone, Copy)]
pub(crate) struct TypeVariableFlags: u8 {
const DIVERGING = 1 << 0;
const INTEGER = 1 << 1;
@@ -230,14 +232,40 @@ impl<'a> InferenceTable<'a> {
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
- pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
- fold_tys(
+ pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ fold_tys_and_consts(
ty,
- |ty, _| match ty.kind(Interner) {
- TyKind::Alias(AliasTy::Projection(proj_ty)) => {
- self.normalize_projection_ty(proj_ty.clone())
- }
- _ => ty,
+ |e, _| match e {
+ Either::Left(ty) => Either::Left(match ty.kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj_ty)) => {
+ self.normalize_projection_ty(proj_ty.clone())
+ }
+ _ => ty,
+ }),
+ Either::Right(c) => Either::Right(match &c.data(Interner).value {
+ chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::UnevaluatedConst(c_id, subst) => {
+ // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable
+ // and registering an obligation. But it needs chalk support, so we handle the most basic
+ // case (a non associated const without generic parameters) manually.
+ if subst.len(Interner) == 0 {
+ if let Ok(eval) = self.db.const_eval((*c_id).into(), subst.clone())
+ {
+ eval
+ } else {
+ unknown_const(c.data(Interner).ty.clone())
+ }
+ } else {
+ unknown_const(c.data(Interner).ty.clone())
+ }
+ }
+ _ => c,
+ },
+ _ => c,
+ }),
},
DebruijnIndex::INNERMOST,
)
@@ -463,7 +491,8 @@ impl<'a> InferenceTable<'a> {
pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
let canonicalized = self.canonicalize(in_env);
- let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value);
+ let solution =
+ self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value);
solution
}
@@ -598,7 +627,11 @@ impl<'a> InferenceTable<'a> {
&mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>,
) -> bool {
- let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone());
+ let solution = self.db.trait_solve(
+ self.trait_env.krate,
+ self.trait_env.block,
+ canonicalized.value.clone(),
+ );
match solution {
Some(Solution::Unique(canonical_subst)) => {
@@ -631,10 +664,13 @@ impl<'a> InferenceTable<'a> {
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
match ty.callable_sig(self.db) {
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
- None => self.callable_sig_from_fn_trait(ty, num_args),
+ None => {
+ let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?;
+ Some((Some(f), args_ty, return_ty))
+ }
}
}
@@ -642,7 +678,7 @@ impl<'a> InferenceTable<'a> {
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(FnTrait, Vec<Ty>, Ty)> {
let krate = self.trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
let trait_data = self.db.trait_data(fn_once_trait);
@@ -676,23 +712,90 @@ impl<'a> InferenceTable<'a> {
};
let trait_env = self.trait_env.env.clone();
+ let mut trait_ref = projection.trait_ref(self.db);
let obligation = InEnvironment {
- goal: projection.trait_ref(self.db).cast(Interner),
- environment: trait_env,
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
- if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
+ if self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
+ .is_some()
+ {
self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
- Some((
- Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
- arg_tys,
- return_ty,
- ))
+ for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+ let fn_x_trait = fn_x.get_id(self.db, krate)?;
+ trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+ let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
+ };
+ let canonical = self.canonicalize(obligation.clone());
+ if self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
+ .is_some()
+ {
+ return Some((fn_x, arg_tys, return_ty));
+ }
+ }
+ unreachable!("It should at least implement FnOnce at this point");
} else {
None
}
}
+
+ pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ fold_tys_and_consts(
+ ty,
+ |x, _| match x {
+ Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
+ Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
+ },
+ DebruijnIndex::INNERMOST,
+ )
+ }
+
+ /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
+ pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
+ match ty.kind(Interner) {
+ TyKind::Error => self.new_type_var(),
+ TyKind::InferenceVar(..) => {
+ let ty_resolved = self.resolve_ty_shallow(&ty);
+ if ty_resolved.is_unknown() {
+ self.new_type_var()
+ } else {
+ ty
+ }
+ }
+ _ => ty,
+ }
+ }
+
+ /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
+ pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
+ let data = c.data(Interner);
+ match &data.value {
+ ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()),
+ // try to evaluate unevaluated const. Replace with new var if const eval failed.
+ crate::ConstScalar::UnevaluatedConst(id, subst) => {
+ if let Ok(eval) = self.db.const_eval(*id, subst.clone()) {
+ eval
+ } else {
+ self.new_const_var(data.ty.clone())
+ }
+ }
+ _ => c,
+ },
+ _ => c,
+ }
+ }
}
impl<'a> fmt::Debug for InferenceTable<'a> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
index 36af78153..e5038543b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -6,9 +6,10 @@ use chalk_ir::{
DebruijnIndex,
};
use hir_def::{
- adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
- ModuleId, VariantId,
+ attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
+ Lookup, ModuleId, VariantId,
};
+use rustc_hash::FxHashSet;
use crate::{
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
@@ -16,7 +17,8 @@ use crate::{
/// Checks whether a type is visibly uninhabited from a particular module.
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
- let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let mut uninhabited_from =
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
@@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
let vars_attrs = db.variants_attrs(variant.parent);
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
- let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let mut uninhabited_from =
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = uninhabited_from.visit_variant(
variant.into(),
&enum_data.variants[variant.local_id].variant_data,
@@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
struct UninhabitedFrom<'a> {
target_mod: ModuleId,
+ recursive_ty: FxHashSet<Ty>,
+ // guard for preventing stack overflow in non trivial non terminating types
+ max_depth: usize,
db: &'a dyn HirDatabase,
}
@@ -65,17 +71,27 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
ty: &Ty,
outer_binder: DebruijnIndex,
) -> ControlFlow<VisiblyUninhabited> {
- match ty.kind(Interner) {
+ if self.recursive_ty.contains(ty) || self.max_depth == 0 {
+ // rustc considers recursive types always inhabited. I think it is valid to consider
+ // recursive types as always uninhabited, but we should do what rustc is doing.
+ return CONTINUE_OPAQUELY_INHABITED;
+ }
+ self.recursive_ty.insert(ty.clone());
+ self.max_depth -= 1;
+ let r = match ty.kind(Interner) {
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
- TyKind::Array(item_ty, len) => match try_const_usize(len) {
+ TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) {
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
Some(1..) => item_ty.super_visit_with(self, outer_binder),
},
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
- }
+ };
+ self.recursive_ty.remove(ty);
+ self.max_depth += 1;
+ r
}
fn interner(&self) -> Interner {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
index aea7e9762..e4dd4b86c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
@@ -7,7 +7,8 @@ use chalk_ir::{Goal, GoalData};
use hir_def::TypeAliasId;
use intern::{impl_internable, Interned};
use smallvec::SmallVec;
-use std::{fmt, sync::Arc};
+use std::fmt;
+use triomphe::Arc;
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Interner;
@@ -43,7 +44,7 @@ impl_internable!(
);
impl chalk_ir::interner::Interner for Interner {
- type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>;
+ type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Self>>>;
type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>;
type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>;
type InternedConcreteConst = ConstScalar;
@@ -51,8 +52,8 @@ impl chalk_ir::interner::Interner for Interner {
type InternedGoal = Arc<GoalData<Self>>;
type InternedGoals = Vec<Goal<Self>>;
type InternedSubstitution = Interned<InternedWrapper<SmallVec<[GenericArg; 2]>>>;
- type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
type InternedProgramClauses = Interned<InternedWrapper<Vec<chalk_ir::ProgramClause<Self>>>>;
+ type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
type InternedQuantifiedWhereClauses =
Interned<InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Self>>>>;
type InternedVariableKinds = Interned<InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>>;
@@ -86,6 +87,27 @@ impl chalk_ir::interner::Interner for Interner {
tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
}
+ fn debug_opaque_ty_id(
+ opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0))
+ }
+
+ fn debug_fn_def_id(
+ fn_def_id: chalk_ir::FnDefId<Self>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
+ }
+
+ fn debug_closure_id(
+ _fn_def_id: chalk_ir::ClosureId<Self>,
+ _fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ None
+ }
+
fn debug_alias(
alias: &chalk_ir::AliasTy<Interner>,
fmt: &mut fmt::Formatter<'_>,
@@ -113,13 +135,6 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
}
- fn debug_opaque_ty_id(
- opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0))
- }
-
fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", ty.data(Interner)))
}
@@ -131,76 +146,56 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", lifetime.data(Interner)))
}
- fn debug_generic_arg(
- parameter: &GenericArg,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug()))
- }
-
- fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
- let goal_data = goal.data(Interner);
- Some(write!(fmt, "{goal_data:?}"))
- }
-
- fn debug_goals(
- goals: &chalk_ir::Goals<Interner>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", goals.debug(Interner)))
- }
-
- fn debug_program_clause_implication(
- pci: &chalk_ir::ProgramClauseImplication<Interner>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", pci.debug(Interner)))
- }
-
- fn debug_substitution(
- substitution: &chalk_ir::Substitution<Interner>,
+ fn debug_const(
+ constant: &chalk_ir::Const<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", substitution.debug(Interner)))
+ Some(write!(fmt, "{:?}", constant.data(Interner)))
}
- fn debug_separator_trait_ref(
- separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>,
+ fn debug_generic_arg(
+ parameter: &GenericArg,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner)))
+ Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug()))
}
- fn debug_fn_def_id(
- fn_def_id: chalk_ir::FnDefId<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
- }
- fn debug_const(
- constant: &chalk_ir::Const<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", constant.data(Interner)))
- }
fn debug_variable_kinds(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", variable_kinds.as_slice(Interner)))
}
+
fn debug_variable_kinds_with_angles(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", variable_kinds.inner_debug(Interner)))
}
+
fn debug_canonical_var_kinds(
canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(Interner)))
}
+ fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
+ let goal_data = goal.data(Interner);
+ Some(write!(fmt, "{goal_data:?}"))
+ }
+ fn debug_goals(
+ goals: &chalk_ir::Goals<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", goals.debug(Interner)))
+ }
+ fn debug_program_clause_implication(
+ pci: &chalk_ir::ProgramClauseImplication<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", pci.debug(Interner)))
+ }
fn debug_program_clause(
clause: &chalk_ir::ProgramClause<Self>,
fmt: &mut fmt::Formatter<'_>,
@@ -213,6 +208,19 @@ impl chalk_ir::interner::Interner for Interner {
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", clauses.as_slice(Interner)))
}
+ fn debug_substitution(
+ substitution: &chalk_ir::Substitution<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", substitution.debug(Interner)))
+ }
+ fn debug_separator_trait_ref(
+ separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner)))
+ }
+
fn debug_quantified_where_clauses(
clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
fmt: &mut fmt::Formatter<'_>,
@@ -220,6 +228,13 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", clauses.as_slice(Interner)))
}
+ fn debug_constraints(
+ _clauses: &chalk_ir::Constraints<Self>,
+ _fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ None
+ }
+
fn intern_ty(self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType {
let flags = kind.compute_flags(self);
Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags }))
@@ -251,7 +266,7 @@ impl chalk_ir::interner::Interner for Interner {
c1: &Self::InternedConcreteConst,
c2: &Self::InternedConcreteConst,
) -> bool {
- (c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
+ !matches!(c1, ConstScalar::Bytes(..)) || !matches!(c2, ConstScalar::Bytes(..)) || (c1 == c2)
}
fn intern_generic_arg(
@@ -272,6 +287,10 @@ impl chalk_ir::interner::Interner for Interner {
Arc::new(goal)
}
+ fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> {
+ goal
+ }
+
fn intern_goals<E>(
self,
data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
@@ -279,10 +298,6 @@ impl chalk_ir::interner::Interner for Interner {
data.into_iter().collect()
}
- fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> {
- goal
- }
-
fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal<Interner>] {
goals
}
@@ -367,32 +382,18 @@ impl chalk_ir::interner::Interner for Interner {
) -> &[chalk_ir::CanonicalVarKind<Self>] {
canonical_var_kinds
}
-
fn intern_constraints<E>(
self,
data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>,
) -> Result<Self::InternedConstraints, E> {
data.into_iter().collect()
}
-
fn constraints_data(
self,
constraints: &Self::InternedConstraints,
) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
constraints
}
- fn debug_closure_id(
- _fn_def_id: chalk_ir::ClosureId<Self>,
- _fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- None
- }
- fn debug_constraints(
- _clauses: &chalk_ir::Constraints<Self>,
- _fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- None
- }
fn intern_variances<E>(
self,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
index 5308c7216..85ed46b96 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
@@ -1,19 +1,65 @@
//! Functions to detect special lang items
-use hir_def::{lang_item::LangItem, AdtId, HasModule};
+use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
+use hir_expand::name::Name;
use crate::db::HirDatabase;
-pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
- let krate = adt.module(db.upcast()).krate();
- let box_adt =
- db.lang_item(krate, LangItem::OwnedBox).and_then(|it| it.as_struct()).map(AdtId::from);
- Some(adt) == box_adt
+pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool {
+ let AdtId::StructId(id) = adt else { return false };
+ db.struct_data(id).flags.contains(StructFlags::IS_BOX)
}
-pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
- let krate = adt.module(db.upcast()).krate();
- let box_adt =
- db.lang_item(krate, LangItem::UnsafeCell).and_then(|it| it.as_struct()).map(AdtId::from);
- Some(adt) == box_adt
+pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool {
+ let AdtId::StructId(id) = adt else { return false };
+ db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL)
+}
+
+pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
+ use hir_expand::name;
+ use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
+ Some(match op {
+ BinaryOp::LogicOp(_) => return None,
+ BinaryOp::ArithOp(aop) => match aop {
+ ArithOp::Add => (name![add], LangItem::Add),
+ ArithOp::Mul => (name![mul], LangItem::Mul),
+ ArithOp::Sub => (name![sub], LangItem::Sub),
+ ArithOp::Div => (name![div], LangItem::Div),
+ ArithOp::Rem => (name![rem], LangItem::Rem),
+ ArithOp::Shl => (name![shl], LangItem::Shl),
+ ArithOp::Shr => (name![shr], LangItem::Shr),
+ ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
+ ArithOp::BitOr => (name![bitor], LangItem::BitOr),
+ ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
+ },
+ BinaryOp::Assignment { op: Some(aop) } => match aop {
+ ArithOp::Add => (name![add_assign], LangItem::AddAssign),
+ ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
+ ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
+ ArithOp::Div => (name![div_assign], LangItem::DivAssign),
+ ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
+ ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
+ ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
+ ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
+ ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
+ ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
+ },
+ BinaryOp::CmpOp(cop) => match cop {
+ CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
+ CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
+ CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
+ (name![le], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
+ (name![lt], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
+ (name![ge], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
+ (name![gt], LangItem::PartialOrd)
+ }
+ },
+ BinaryOp::Assignment { op: None } => return None,
+ })
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index b95bb01fc..35d3407c1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -1,19 +1,23 @@
//! Compute the binary representation of a type
use base_db::CrateId;
-use chalk_ir::{AdtId, TyKind};
+use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
use hir_def::{
layout::{
- Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions,
- RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange,
+ Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size,
+ StructKind, TargetDataLayout, WrappingRange,
},
- LocalFieldId,
+ LocalEnumVariantId, LocalFieldId,
};
+use la_arena::{Idx, RawIdx};
use stdx::never;
+use triomphe::Arc;
-use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty};
+use crate::{
+ consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
+ utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
+};
-use self::adt::struct_variant_idx;
pub use self::{
adt::{layout_of_adt_query, layout_of_adt_recover},
target::target_data_layout_query,
@@ -28,6 +32,34 @@ macro_rules! user_error {
mod adt;
mod target;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
+
+impl rustc_index::vec::Idx for RustcEnumVariantIdx {
+ fn new(idx: usize) -> Self {
+ RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
+ }
+
+ fn index(self) -> usize {
+ u32::from(self.0.into_raw()) as usize
+ }
+}
+
+pub type Layout = LayoutS<RustcEnumVariantIdx>;
+pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
+pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum LayoutError {
+ UserError(String),
+ SizeOverflow,
+ TargetLayoutNotAvailable,
+ HasPlaceholder,
+ HasErrorType,
+ NotImplemented,
+ Unknown,
+}
+
struct LayoutCx<'a> {
krate: CrateId,
target: &'a TargetDataLayout,
@@ -45,20 +77,18 @@ impl<'a> LayoutCalculator for LayoutCx<'a> {
}
}
-fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
- Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
-}
-
-fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
- Layout::scalar(dl, scalar_unit(dl, value))
-}
-
-pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
+pub fn layout_of_ty_query(
+ db: &dyn HirDatabase,
+ ty: Ty,
+ krate: CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target };
let dl = &*cx.current_data_layout();
- Ok(match ty.kind(Interner) {
- TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
+ let trait_env = Arc::new(TraitEnvironment::empty(krate));
+ let ty = normalize(db, trait_env, ty.clone());
+ let result = match ty.kind(Interner) {
+ TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate),
TyKind::Scalar(s) => match s {
chalk_ir::Scalar::Bool => Layout::scalar(
dl,
@@ -78,12 +108,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
dl,
Primitive::Int(
match i {
- chalk_ir::IntTy::Isize => dl.ptr_sized_integer(),
- chalk_ir::IntTy::I8 => Integer::I8,
- chalk_ir::IntTy::I16 => Integer::I16,
- chalk_ir::IntTy::I32 => Integer::I32,
- chalk_ir::IntTy::I64 => Integer::I64,
- chalk_ir::IntTy::I128 => Integer::I128,
+ IntTy::Isize => dl.ptr_sized_integer(),
+ IntTy::I8 => Integer::I8,
+ IntTy::I16 => Integer::I16,
+ IntTy::I32 => Integer::I32,
+ IntTy::I64 => Integer::I64,
+ IntTy::I128 => Integer::I128,
},
true,
),
@@ -92,12 +122,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
dl,
Primitive::Int(
match i {
- chalk_ir::UintTy::Usize => dl.ptr_sized_integer(),
- chalk_ir::UintTy::U8 => Integer::I8,
- chalk_ir::UintTy::U16 => Integer::I16,
- chalk_ir::UintTy::U32 => Integer::I32,
- chalk_ir::UintTy::U64 => Integer::I64,
- chalk_ir::UintTy::U128 => Integer::I128,
+ UintTy::Usize => dl.ptr_sized_integer(),
+ UintTy::U8 => Integer::I8,
+ UintTy::U16 => Integer::I16,
+ UintTy::U32 => Integer::I32,
+ UintTy::U64 => Integer::I64,
+ UintTy::U128 => Integer::I128,
},
false,
),
@@ -105,8 +135,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
chalk_ir::Scalar::Float(f) => scalar(
dl,
match f {
- chalk_ir::FloatTy::F32 => Primitive::F32,
- chalk_ir::FloatTy::F64 => Primitive::F64,
+ FloatTy::F32 => Primitive::F32,
+ FloatTy::F64 => Primitive::F64,
},
),
},
@@ -115,17 +145,17 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
let fields = tys
.iter(Interner)
- .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
+ .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate))
.collect::<Result<Vec<_>, _>>()?;
- let fields = fields.iter().collect::<Vec<_>>();
+ let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>();
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
}
TyKind::Array(element, count) => {
- let count = try_const_usize(&count).ok_or(LayoutError::UserError(
- "mismatched type of const generic parameter".to_string(),
+ let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(
+ "unevaluated or mistyped const generic parameter".to_string(),
))? as u64;
- let element = layout_of_ty(db, element, krate)?;
+ let element = db.layout_of_ty(element.clone(), krate)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
@@ -146,7 +176,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
}
}
TyKind::Slice(element) => {
- let element = layout_of_ty(db, element, krate)?;
+ let element = db.layout_of_ty(element.clone(), krate)?;
Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: element.size, count: 0 },
@@ -180,7 +210,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
}
_ => {
// pointee is sized
- return Ok(Layout::scalar(dl, data_ptr));
+ return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
}
};
@@ -222,23 +252,51 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
match impl_trait_id {
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
let infer = db.infer(func.into());
- layout_of_ty(db, &infer.type_of_rpit[idx], krate)?
+ return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
return Err(LayoutError::NotImplemented)
}
}
}
- TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
+ TyKind::Closure(c, subst) => {
+ let (def, _) = db.lookup_intern_closure((*c).into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(c);
+ let fields = captures
+ .iter()
+ .map(|x| {
+ db.layout_of_ty(
+ x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
+ krate,
+ )
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+ let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
+ let fields = fields.iter().collect::<Vec<_>>();
+ cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
+ .ok_or(LayoutError::Unknown)?
+ }
+ TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
return Err(LayoutError::NotImplemented)
}
+ TyKind::Error => return Err(LayoutError::HasErrorType),
TyKind::AssociatedType(_, _)
- | TyKind::Error
| TyKind::Alias(_)
| TyKind::Placeholder(_)
| TyKind::BoundVar(_)
| TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
- })
+ };
+ Ok(Arc::new(result))
+}
+
+pub fn layout_of_ty_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &Ty,
+ _: &CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
+ user_error!("infinite sized recursive type");
}
fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
@@ -274,5 +332,13 @@ fn field_ty(
db.field_types(def)[fd].clone().substitute(Interner, subst)
}
+fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
+ Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
+}
+
+fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
+ Layout::scalar(dl, scalar_unit(dl, value))
+}
+
#[cfg(test)]
mod tests;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index b22d0fe8d..bd2752a71 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -1,18 +1,25 @@
//! Compute the binary representation of structs, unions and enums
-use std::ops::Bound;
+use std::{cmp, ops::Bound};
+use base_db::CrateId;
use hir_def::{
- adt::VariantData,
- layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
- AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
+ data::adt::VariantData,
+ layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
+ AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;
+use triomphe::Arc;
-use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
+use crate::{
+ db::HirDatabase,
+ lang_items::is_unsafe_cell,
+ layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx},
+ Substitution,
+};
-use super::{layout_of_ty, LayoutCx};
+use super::LayoutCx;
pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
@@ -22,29 +29,29 @@ pub fn layout_of_adt_query(
db: &dyn HirDatabase,
def: AdtId,
subst: Substitution,
-) -> Result<Layout, LayoutError> {
- let krate = def.module(db.upcast()).krate();
+ krate: CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target };
let dl = cx.current_data_layout();
let handle_variant = |def: VariantId, var: &VariantData| {
var.fields()
.iter()
- .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
+ .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate))
.collect::<Result<Vec<_>, _>>()
};
- let (variants, is_enum, is_union, repr) = match def {
+ let (variants, repr) = match def {
AdtId::StructId(s) => {
let data = db.struct_data(s);
let mut r = SmallVec::<[_; 1]>::new();
r.push(handle_variant(s.into(), &data.variant_data)?);
- (r, false, false, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
AdtId::UnionId(id) => {
let data = db.union_data(id);
let mut r = SmallVec::new();
r.push(handle_variant(id.into(), &data.variant_data)?);
- (r, false, true, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
AdtId::EnumId(e) => {
let data = db.enum_data(e);
@@ -58,22 +65,24 @@ pub fn layout_of_adt_query(
)
})
.collect::<Result<SmallVec<_>, _>>()?;
- (r, true, false, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
};
- let variants =
- variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
+ let variants = variants
+ .iter()
+ .map(|x| x.iter().map(|x| &**x).collect::<Vec<_>>())
+ .collect::<SmallVec<[_; 1]>>();
let variants = variants.iter().map(|x| x.iter().collect()).collect();
- if is_union {
- cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
+ let result = if matches!(def, AdtId::UnionId(..)) {
+ cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
} else {
cx.layout_of_struct_or_enum(
&repr,
&variants,
- is_enum,
- is_unsafe_cell(def, db),
+ matches!(def, AdtId::EnumId(..)),
+ is_unsafe_cell(db, def),
layout_scalar_valid_range(db, def),
- |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
+ |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
variants.iter_enumerated().filter_map(|(id, _)| {
let AdtId::EnumId(e) = def else { return None };
let d =
@@ -90,15 +99,16 @@ pub fn layout_of_adt_query(
// .iter_enumerated()
// .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
repr.inhibit_enum_layout_opt(),
- !is_enum
+ !matches!(def, AdtId::EnumId(..))
&& variants
.iter()
.next()
.and_then(|x| x.last().map(|x| x.is_unsized()))
.unwrap_or(true),
)
- .ok_or(LayoutError::SizeOverflow)
- }
+ .ok_or(LayoutError::SizeOverflow)?
+ };
+ Ok(Arc::new(result))
}
fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
@@ -122,6 +132,54 @@ pub fn layout_of_adt_recover(
_: &[String],
_: &AdtId,
_: &Substitution,
-) -> Result<Layout, LayoutError> {
+ _: &CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
user_error!("infinite sized recursive type");
}
+
+/// Finds the appropriate Integer type and signedness for the given
+/// signed discriminant range and `#[repr]` attribute.
+/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
+/// that shouldn't affect anything, other than maybe debuginfo.
+fn repr_discr(
+ dl: &TargetDataLayout,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+) -> Result<(Integer, bool), LayoutError> {
+ // Theoretically, negative values could be larger in unsigned representation
+ // than the unsigned representation of the signed minimum. However, if there
+ // are any negative values, the only valid unsigned representation is u128
+ // which can fit all i128 values, so the result remains unaffected.
+ let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
+ let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+ if let Some(ity) = repr.int {
+ let discr = Integer::from_attr(dl, ity);
+ let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+ if discr < fit {
+ return Err(LayoutError::UserError(
+ "Integer::repr_discr: `#[repr]` hint too small for \
+ discriminant range of enum "
+ .to_string(),
+ ));
+ }
+ return Ok((discr, ity.is_signed()));
+ }
+
+ let at_least = if repr.c() {
+ // This is usually I32, however it can be different on some platforms,
+ // notably hexagon and arm-none/thumb-none
+ dl.c_enum_min_size
+ } else {
+ // repr(Rust) enums try to be as small as possible
+ Integer::I8
+ };
+
+ // If there are no negative values, we can use the unsigned fit.
+ Ok(if min >= 0 {
+ (cmp::max(unsigned_fit, at_least), false)
+ } else {
+ (cmp::max(signed_fit, at_least), true)
+ })
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
index adfae0a1a..04b940afb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
@@ -1,9 +1,8 @@
//! Target dependent parameters needed for layouts
-use std::sync::Arc;
-
use base_db::CrateId;
use hir_def::layout::TargetDataLayout;
+use triomphe::Arc;
use crate::db::HirDatabase;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index a8971fde3..0ff8c532d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -2,49 +2,67 @@ use std::collections::HashMap;
use base_db::fixture::WithFixture;
use chalk_ir::{AdtId, TyKind};
-use hir_def::{
- db::DefDatabase,
+use either::Either;
+use hir_def::db::DefDatabase;
+use triomphe::Arc;
+
+use crate::{
+ db::HirDatabase,
layout::{Layout, LayoutError},
+ test_db::TestDB,
+ Interner, Substitution,
};
-use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
-
-use super::layout_of_ty;
+mod closure;
fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
}
-fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
);
- let (db, file_id) = TestDB::with_single_file(&ra_fixture);
- let module_id = db.module_for_file(file_id);
- let def_map = module_id.def_map(&db);
- let scope = &def_map[module_id.local_id].scope;
- let adt_id = scope
- .declarations()
- .find_map(|x| match x {
- hir_def::ModuleDefId::AdtId(x) => {
- let name = match x {
- hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
- hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
- hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
- };
- (name == "Goal").then_some(x)
- }
- _ => None,
+ let (db, file_ids) = TestDB::with_many_files(&ra_fixture);
+ let (adt_or_type_alias_id, module_id) = file_ids
+ .into_iter()
+ .find_map(|file_id| {
+ let module_id = db.module_for_file(file_id);
+ let def_map = module_id.def_map(&db);
+ let scope = &def_map[module_id.local_id].scope;
+ let adt_or_type_alias_id = scope.declarations().find_map(|x| match x {
+ hir_def::ModuleDefId::AdtId(x) => {
+ let name = match x {
+ hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
+ hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
+ hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
+ };
+ (name == "Goal").then_some(Either::Left(x))
+ }
+ hir_def::ModuleDefId::TypeAliasId(x) => {
+ let name = db.type_alias_data(x).name.to_smol_str();
+ (name == "Goal").then_some(Either::Right(x))
+ }
+ _ => None,
+ })?;
+ Some((adt_or_type_alias_id, module_id))
})
.unwrap();
- let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
- layout_of_ty(&db, &goal_ty, module_id.krate())
+ let goal_ty = match adt_or_type_alias_id {
+ Either::Left(adt_id) => {
+ TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner)
+ }
+ Either::Right(ty_id) => {
+ db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner))
+ }
+ };
+ db.layout_of_ty(goal_ty, module_id.krate())
}
/// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
-fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}",
@@ -68,7 +86,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
let infer = db.infer(adt_id.into());
let goal_ty = infer.type_of_binding[b].clone();
- layout_of_ty(&db, &goal_ty, module_id.krate())
+ db.layout_of_ty(goal_ty, module_id.krate())
}
#[track_caller]
@@ -81,8 +99,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
#[track_caller]
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_expr(ra_fixture, minicore).unwrap();
- assert_eq!(l.size.bytes(), size);
- assert_eq!(l.align.abi.bytes(), align);
+ assert_eq!(l.size.bytes(), size, "size mismatch");
+ assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
@@ -118,13 +136,31 @@ macro_rules! size_and_align {
};
}
+#[macro_export]
macro_rules! size_and_align_expr {
+ (minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
+ {
+ #[allow(dead_code)]
+ #[allow(unused_must_use)]
+ #[allow(path_statements)]
+ {
+ $($s)*
+ let val = { $($t)* };
+ $crate::layout::tests::check_size_and_align_expr(
+ &format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
+ &format!("//- minicore: {}\n", stringify!($($x),*)),
+ ::std::mem::size_of_val(&val) as u64,
+ ::std::mem::align_of_val(&val) as u64,
+ );
+ }
+ }
+ };
($($t:tt)*) => {
{
#[allow(dead_code)]
{
let val = { $($t)* };
- check_size_and_align_expr(
+ $crate::layout::tests::check_size_and_align_expr(
stringify!($($t)*),
"",
::std::mem::size_of_val(&val) as u64,
@@ -197,6 +233,44 @@ fn generic() {
}
#[test]
+fn associated_types() {
+ size_and_align! {
+ trait Tr {
+ type Ty;
+ }
+
+ impl Tr for i32 {
+ type Ty = i64;
+ }
+
+ struct Foo<A: Tr>(<A as Tr>::Ty);
+ struct Bar<A: Tr>(A::Ty);
+ struct Goal(Foo<i32>, Bar<i32>, <i32 as Tr>::Ty);
+ }
+ check_size_and_align(
+ r#"
+//- /b/mod.rs crate:b
+pub trait Tr {
+ type Ty;
+}
+pub struct Foo<A: Tr>(<A as Tr>::Ty);
+
+//- /a/mod.rs crate:a deps:b
+use b::{Tr, Foo};
+
+struct S;
+impl Tr for S {
+ type Ty = i64;
+}
+struct Goal(Foo<S>);
+ "#,
+ "",
+ 8,
+ 8,
+ );
+}
+
+#[test]
fn return_position_impl_trait() {
size_and_align_expr! {
trait T {}
@@ -213,6 +287,45 @@ fn return_position_impl_trait() {
foo()
}
size_and_align_expr! {
+ minicore: iterators;
+ stmts: []
+ trait Tr {}
+ impl Tr for i32 {}
+ fn foo() -> impl Iterator<Item = impl Tr> {
+ [1, 2, 3].into_iter()
+ }
+ let mut iter = foo();
+ let item = iter.next();
+ (iter, item)
+ }
+ size_and_align_expr! {
+ minicore: future;
+ stmts: []
+ use core::{future::Future, task::{Poll, Context}, pin::pin};
+ use std::{task::Wake, sync::Arc};
+ trait Tr {}
+ impl Tr for i32 {}
+ async fn f() -> impl Tr {
+ 2
+ }
+ fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
+ // In a normal test we could use `loop {}` or `panic!()` here,
+ // but rustc actually runs this code.
+ let pinned = pin!(inp);
+ struct EmptyWaker;
+ impl Wake for EmptyWaker {
+ fn wake(self: Arc<Self>) {
+ }
+ }
+ let waker = Arc::new(EmptyWaker).into();
+ let mut context = Context::from_waker(&waker);
+ let x = pinned.poll(&mut context);
+ x
+ }
+ let x = unwrap_fut(f());
+ x
+ }
+ size_and_align_expr! {
struct Foo<T>(T, T, (T, T));
trait T {}
impl T for Foo<i32> {}
@@ -277,6 +390,27 @@ fn niche_optimization() {
}
#[test]
+fn const_eval() {
+ size_and_align! {
+ struct Goal([i32; 2 + 2]);
+ }
+ size_and_align! {
+ const X: usize = 5;
+ struct Goal([i32; X]);
+ }
+ size_and_align! {
+ mod foo {
+ pub(super) const BAR: usize = 5;
+ }
+ struct Ar<T>([T; foo::BAR]);
+ struct Goal(Ar<Ar<i32>>);
+ }
+ size_and_align! {
+ type Goal = [u8; 2 + 2];
+ }
+}
+
+#[test]
fn enums_with_discriminants() {
size_and_align! {
enum Goal {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
new file mode 100644
index 000000000..576e7f3fc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
@@ -0,0 +1,257 @@
+use crate::size_and_align_expr;
+
+#[test]
+fn zero_capture_simple() {
+ size_and_align_expr! {
+ |x: i32| x + 2
+ }
+}
+
+#[test]
+fn move_simple() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: []
+ let y: i32 = 5;
+ move |x: i32| {
+ x + y
+ }
+ }
+}
+
+#[test]
+fn ref_simple() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let y: i32 = 5;
+ ]
+ |x: i32| {
+ x + y
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let mut y: i32 = 5;
+ ]
+ |x: i32| {
+ y = y + x;
+ y
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy, deref_mut;
+ stmts: [
+ let y: &mut i32 = &mut 5;
+ ]
+ |x: i32| {
+ *y += x;
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i32, i64);
+ let x: X = X(2, 6);
+ ]
+ || {
+ x
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy, deref_mut;
+ stmts: [
+ struct X(i32, i64);
+ let x: &mut X = &mut X(2, 6);
+ ]
+ || {
+ (*x).0 as i64 + x.1
+ }
+ }
+}
+
+#[test]
+fn ref_then_mut_then_move() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i32, i64);
+ let mut x: X = X(2, 6);
+ ]
+ || {
+ &x;
+ &mut x;
+ x;
+ }
+ }
+}
+
+#[test]
+fn nested_closures() {
+ size_and_align_expr! {
+ || {
+ || {
+ || {
+ let x = 2;
+ move || {
+ move || {
+ x
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn capture_specific_fields2() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let x = &mut 2;
+ ]
+ || {
+ *x = 5;
+ &x;
+ }
+ }
+}
+
+#[test]
+fn capture_specific_fields() {
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let _ = &y;
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ let y = &y;
+ move |x: i64| {
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a, _, (b, _)) = y;
+ a + x + (b as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y = &&X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a, _, (b, _)) = y;
+ *a + x + (*b as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ match y {
+ X(a, _, (b, _)) => a + x + (b as i64),
+ }
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a @ 2, _, (b, _)) = y else { return 5 };
+ a + x + (b as i64)
+ }
+ }
+}
+
+#[test]
+fn match_pattern() {
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ match y {
+ _ => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ X(_a, _, _c) => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ _y => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ ref _y => x,
+ }
+ }
+ }
+}
+
+#[test]
+fn ellipsis_pattern() {
+ size_and_align_expr! {
+ struct X(i8, u16, i32, u64, i128, u8);
+ let y: X = X(1, 2, 3, 4, 5, 6);
+ move |_: i64| {
+ let X(_a, .., _b, _c) = y;
+ }
+ }
+ size_and_align_expr! {
+ struct X { a: i32, b: u8, c: i128}
+ let y: X = X { a: 1, b: 2, c: 3 };
+ move |_: i64| {
+ let X { a, b, .. } = y;
+ _ = (a, b);
+ }
+ }
+ size_and_align_expr! {
+ let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6);
+ move |_: i64| {
+ let ((_a, .., _b, _c), .., _e, _f) = y;
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 9c63d67ab..1a4d003bf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -1,6 +1,5 @@
//! The type system. We currently use this to infer types for completion, hover
//! information and various assists.
-
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
#[allow(unused)]
@@ -8,12 +7,9 @@ macro_rules! eprintln {
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}
-mod autoderef;
mod builder;
mod chalk_db;
mod chalk_ext;
-pub mod consteval;
-pub mod mir;
mod infer;
mod inhabitedness;
mod interner;
@@ -21,21 +17,28 @@ mod lower;
mod mapping;
mod tls;
mod utils;
+
+pub mod autoderef;
+pub mod consteval;
pub mod db;
pub mod diagnostics;
pub mod display;
+pub mod lang_items;
+pub mod layout;
pub mod method_resolution;
+pub mod mir;
pub mod primitive;
pub mod traits;
-pub mod layout;
-pub mod lang_items;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod test_db;
-use std::{collections::HashMap, hash::Hash, sync::Arc};
+use std::{
+ collections::{hash_map::Entry, HashMap},
+ hash::Hash,
+};
use chalk_ir::{
fold::{Shift, TypeFoldable},
@@ -44,12 +47,13 @@ use chalk_ir::{
NoSolution, TyData,
};
use either::Either;
-use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
+use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId};
use hir_expand::name;
use la_arena::{Arena, Idx};
-use mir::MirEvalError;
+use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet;
use traits::FnTrait;
+use triomphe::Arc;
use utils::Generics;
use crate::{
@@ -60,6 +64,7 @@ pub use autoderef::autoderef;
pub use builder::{ParamKind, TyBuilder};
pub use chalk_ext::*;
pub use infer::{
+ closure::{CaptureKind, CapturedItem},
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
InferenceResult, OverloadedDeref, PointerCast,
};
@@ -148,14 +153,26 @@ pub type Guidance = chalk_solve::Guidance<Interner>;
pub type WhereClause = chalk_ir::WhereClause<Interner>;
/// A constant can have reference to other things. Memory map job is holding
-/// the neccessary bits of memory of the const eval session to keep the constant
+/// the necessary bits of memory of the const eval session to keep the constant
/// meaningful.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
-pub struct MemoryMap(pub HashMap<usize, Vec<u8>>);
+pub struct MemoryMap {
+ pub memory: HashMap<usize, Vec<u8>>,
+ pub vtable: VTableMap,
+}
impl MemoryMap {
fn insert(&mut self, addr: usize, x: Vec<u8>) {
- self.0.insert(addr, x);
+ match self.memory.entry(addr) {
+ Entry::Occupied(mut e) => {
+ if e.get().len() < x.len() {
+ e.insert(x);
+ }
+ }
+ Entry::Vacant(e) => {
+ e.insert(x);
+ }
+ }
}
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
@@ -165,7 +182,15 @@ impl MemoryMap {
&self,
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
) -> Result<HashMap<usize, usize>, MirEvalError> {
- self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+ self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+ }
+
+ fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> {
+ if size == 0 {
+ Some(&[])
+ } else {
+ self.memory.get(&addr)?.get(0..size)
+ }
}
}
@@ -173,6 +198,9 @@ impl MemoryMap {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstScalar {
Bytes(Vec<u8>, MemoryMap),
+ // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
+ // constants
+ UnevaluatedConst(GeneralConstId, Substitution),
/// Case of an unknown value that rustc might know but we don't
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
// constants
@@ -283,16 +311,19 @@ impl CallableSig {
pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
CallableSig {
// FIXME: what to do about lifetime params? -> return PolyFnSig
- params_and_return: fn_ptr
- .substitution
- .clone()
- .shifted_out_to(Interner, DebruijnIndex::ONE)
- .expect("unexpected lifetime vars in fn ptr")
- .0
- .as_slice(Interner)
- .iter()
- .map(|arg| arg.assert_ty_ref(Interner).clone())
- .collect(),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ params_and_return: Arc::from(
+ fn_ptr
+ .substitution
+ .clone()
+ .shifted_out_to(Interner, DebruijnIndex::ONE)
+ .expect("unexpected lifetime vars in fn ptr")
+ .0
+ .as_slice(Interner)
+ .iter()
+ .map(|arg| arg.assert_ty_ref(Interner).clone())
+ .collect::<Vec<_>>(),
+ ),
is_varargs: fn_ptr.sig.variadic,
safety: fn_ptr.sig.safety,
}
@@ -576,15 +607,19 @@ where
}
pub fn callable_sig_from_fnonce(
- self_ty: &Ty,
+ mut self_ty: &Ty,
env: Arc<TraitEnvironment>,
db: &dyn HirDatabase,
) -> Option<CallableSig> {
+ if let Some((ty, _, _)) = self_ty.as_reference() {
+ // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
+ self_ty = ty;
+ }
let krate = env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
- let mut table = InferenceTable::new(db, env.clone());
+ let mut table = InferenceTable::new(db, env);
let b = TyBuilder::trait_ref(db, fn_once_trait);
if b.remaining() != 2 {
return None;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 23b15087e..9951a1c75 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -8,7 +8,6 @@
use std::{
cell::{Cell, RefCell, RefMut},
iter,
- sync::Arc,
};
use base_db::CrateId;
@@ -18,19 +17,21 @@ use chalk_ir::{
use either::Either;
use hir_def::{
- adt::StructKind,
- body::{Expander, LowerCtx},
builtin_type::BuiltinType,
+ data::adt::StructKind,
+ expander::Expander,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
lang_item::{lang_attr, LangItem},
- path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
+ nameres::MacroSubNs,
+ path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
- type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
- AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
- HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
- TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
+ type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
+ AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
+ GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup,
+ ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
+ TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::Name, ExpandResult};
use intern::Interned;
@@ -39,20 +40,28 @@ use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::ast;
+use triomphe::Arc;
use crate::{
all_super_traits,
- consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
+ consteval::{
+ intern_const_ref, intern_const_scalar, path_to_const, unknown_const,
+ unknown_const_as_generic,
+ },
db::HirDatabase,
make_binders,
mapping::{from_chalk_trait_id, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::Generics,
- utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
- AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnPointer,
- FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
- QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
- Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+ utils::{
+ all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
+ InTypeConstIdMetadata,
+ },
+ AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
+ FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
+ ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
+ ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+ TyKind, WhereClause,
};
#[derive(Debug)]
@@ -103,8 +112,9 @@ impl ImplTraitLoweringState {
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
- pub resolver: &'a Resolver,
+ resolver: &'a Resolver,
in_binders: DebruijnIndex,
+ owner: TypeOwnerId,
/// Note: Conceptually, it's thinkable that we could be in a location where
/// some type params should be represented as placeholders, and others
/// should be converted to variables. I think in practice, this isn't
@@ -117,13 +127,14 @@ pub struct TyLoweringContext<'a> {
}
impl<'a> TyLoweringContext<'a> {
- pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
+ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
let impl_trait_mode = ImplTraitLoweringState::Disallowed;
let type_param_mode = ParamLoweringMode::Placeholder;
let in_binders = DebruijnIndex::INNERMOST;
Self {
db,
resolver,
+ owner,
in_binders,
impl_trait_mode,
type_param_mode,
@@ -234,6 +245,7 @@ impl<'a> TyLoweringContext<'a> {
let const_len = const_or_path_to_chalk(
self.db,
self.resolver,
+ self.owner,
TyBuilder::usize(),
len,
self.type_param_mode,
@@ -378,10 +390,19 @@ impl<'a> TyLoweringContext<'a> {
};
let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
- match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+ let resolver = |path| {
+ self.resolver.resolve_path_as_macro(
+ self.db.upcast(),
+ &path,
+ Some(MacroSubNs::Bang),
+ )
+ };
+ match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
+ {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
- let type_ref = TypeRef::from_ast(&ctx, expanded);
+ let ctx = expander.ctx(self.db.upcast());
+ // FIXME: Report syntax errors in expansion here
+ let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
drop(expander);
let ty = self.lower_ty(&type_ref);
@@ -425,11 +446,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 {
return None;
}
- let resolution =
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
- Some((it, None)) => it,
- _ => return None,
- };
+ let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
+ Some((it, None)) => it,
+ _ => return None,
+ };
match resolution {
TypeNs::GenericParam(param_id) => Some(param_id.into()),
_ => None,
@@ -608,7 +628,7 @@ impl<'a> TyLoweringContext<'a> {
}
let (resolution, remaining_index) =
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
@@ -716,7 +736,7 @@ impl<'a> TyLoweringContext<'a> {
resolved: ValueTyDefId,
infer_args: bool,
) -> Substitution {
- let last = path.segments().last().expect("path should have at least one segment");
+ let last = path.segments().last();
let (segment, generic_def) = match resolved {
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
ValueTyDefId::StructId(it) => (last, Some(it.into())),
@@ -732,13 +752,20 @@ impl<'a> TyLoweringContext<'a> {
let len = path.segments().len();
let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
let segment = match penultimate {
- Some(segment) if segment.args_and_bindings.is_some() => segment,
+ Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
_ => last,
};
(segment, Some(var.parent.into()))
}
};
- self.substs_from_path_segment(segment, generic_def, infer_args, None)
+ if let Some(segment) = segment {
+ self.substs_from_path_segment(segment, generic_def, infer_args, None)
+ } else if let Some(generic_def) = generic_def {
+ // lang item
+ self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
+ } else {
+ Substitution::empty(Interner)
+ }
}
fn substs_from_path_segment(
@@ -748,6 +775,21 @@ impl<'a> TyLoweringContext<'a> {
infer_args: bool,
explicit_self_ty: Option<Ty>,
) -> Substitution {
+ self.substs_from_args_and_bindings(
+ segment.args_and_bindings,
+ def,
+ infer_args,
+ explicit_self_ty,
+ )
+ }
+
+ fn substs_from_args_and_bindings(
+ &self,
+ args_and_bindings: Option<&GenericArgs>,
+ def: Option<GenericDefId>,
+ infer_args: bool,
+ explicit_self_ty: Option<Ty>,
+ ) -> Substitution {
// Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new();
let def = if let Some(d) = def {
@@ -780,7 +822,7 @@ impl<'a> TyLoweringContext<'a> {
};
let mut had_explicit_args = false;
- if let Some(generic_args) = &segment.args_and_bindings {
+ if let Some(generic_args) = &args_and_bindings {
if !generic_args.has_self_type {
fill_self_params();
}
@@ -809,6 +851,7 @@ impl<'a> TyLoweringContext<'a> {
const_or_path_to_chalk(
self.db,
self.resolver,
+ self.owner,
ty,
c,
self.type_param_mode,
@@ -879,12 +922,11 @@ impl<'a> TyLoweringContext<'a> {
path: &Path,
explicit_self_ty: Option<Ty>,
) -> Option<TraitRef> {
- let resolved =
- match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
- // FIXME(trait_alias): We need to handle trait alias here.
- TypeNs::TraitId(tr) => tr,
- _ => return None,
- };
+ let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
+ // FIXME(trait_alias): We need to handle trait alias here.
+ TypeNs::TraitId(tr) => tr,
+ _ => return None,
+ };
let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
}
@@ -968,7 +1010,7 @@ impl<'a> TyLoweringContext<'a> {
// ignore `T: Drop` or `T: Destruct` bounds.
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
// (So ideally, we'd only ignore `~const Drop` here)
- // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
+ // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
// the builtin impls are supported by Chalk, we ignore them here.
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
@@ -1062,23 +1104,23 @@ impl<'a> TyLoweringContext<'a> {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution,
};
- let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
+ let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = &binding.type_ref {
let ty = self.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
- preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+ predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
}
for bound in binding.bounds.iter() {
- preds.extend(self.lower_type_bound(
+ predicates.extend(self.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
false,
));
}
- preds
+ predicates
})
}
@@ -1145,7 +1187,7 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
- // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+ // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
// bounds. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup();
@@ -1326,8 +1368,8 @@ pub(crate) fn field_types_query(
};
let generics = generics(db.upcast(), def);
let mut res = ArenaMap::default();
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, GenericDefId::from(variant_id.adt_id()).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() {
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
}
@@ -1349,8 +1391,8 @@ pub(crate) fn generic_predicates_for_param_query(
assoc_name: Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generics = generics(db.upcast(), def);
let mut predicates: Vec<_> = resolver
.where_predicates_in_scope()
@@ -1381,9 +1423,7 @@ pub(crate) fn generic_predicates_for_param_query(
Some(it) => it,
None => return true,
};
- let tr = match resolver
- .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
- {
+ let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
Some(TypeNs::TraitId(tr)) => tr,
_ => return false,
};
@@ -1420,7 +1460,19 @@ pub(crate) fn generic_predicates_for_param_recover(
_param_id: &TypeOrConstParamId,
_assoc_name: &Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
- Arc::new([])
+ // FIXME: use `Arc::from_iter` when it becomes available
+ Arc::from(vec![])
+}
+
+pub(crate) fn trait_environment_for_body_query(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Arc<TraitEnvironment> {
+ let Some(def) = def.as_generic_def_id() else {
+ let krate = def.module(db.upcast()).krate();
+ return Arc::new(TraitEnvironment::empty(krate));
+ };
+ db.trait_environment(def)
}
pub(crate) fn trait_environment_query(
@@ -1428,8 +1480,8 @@ pub(crate) fn trait_environment_query(
def: GenericDefId,
) -> Arc<TraitEnvironment> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Placeholder);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Placeholder);
let mut traits_in_scope = Vec::new();
let mut clauses = Vec::new();
for pred in resolver.where_predicates_in_scope() {
@@ -1478,7 +1530,7 @@ pub(crate) fn trait_environment_query(
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
- Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
+ Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env })
}
/// Resolve the where clause(s) of an item with generics.
@@ -1487,8 +1539,8 @@ pub(crate) fn generic_predicates_query(
def: GenericDefId,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generics = generics(db.upcast(), def);
let mut predicates = resolver
@@ -1542,35 +1594,38 @@ pub(crate) fn generic_defaults_query(
def: GenericDefId,
) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generic_params = generics(db.upcast(), def);
let parent_start_idx = generic_params.len_self();
- let defaults = generic_params
- .iter()
- .enumerate()
- .map(|(idx, (id, p))| {
- let p = match p {
- TypeOrConstParamData::TypeParamData(p) => p,
- TypeOrConstParamData::ConstParamData(_) => {
- // FIXME: implement const generic defaults
- let val = unknown_const_as_generic(
- db.const_param_ty(ConstParamId::from_unchecked(id)),
- );
- return make_binders(db, &generic_params, val);
- }
- };
- let mut ty =
- p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-
- // Each default can only refer to previous parameters.
- // Type variable default referring to parameter coming
- // after it is forbidden (FIXME: report diagnostic)
- ty = fallback_bound_vars(ty, idx, parent_start_idx);
- crate::make_binders(db, &generic_params, ty.cast(Interner))
- })
- .collect();
+ let defaults = Arc::from(
+ generic_params
+ .iter()
+ .enumerate()
+ .map(|(idx, (id, p))| {
+ let p = match p {
+ TypeOrConstParamData::TypeParamData(p) => p,
+ TypeOrConstParamData::ConstParamData(_) => {
+ // FIXME: implement const generic defaults
+ let val = unknown_const_as_generic(
+ db.const_param_ty(ConstParamId::from_unchecked(id)),
+ );
+ return make_binders(db, &generic_params, val);
+ }
+ };
+ let mut ty =
+ p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+
+ // Each default can only refer to previous parameters.
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
defaults
}
@@ -1583,18 +1638,21 @@ pub(crate) fn generic_defaults_recover(
let generic_params = generics(db.upcast(), *def);
// FIXME: this code is not covered in tests.
// we still need one default per parameter
- let defaults = generic_params
- .iter_id()
- .map(|id| {
- let val = match id {
- Either::Left(_) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
- Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
- };
- crate::make_binders(db, &generic_params, val)
- })
- .collect();
+ let defaults = Arc::from(
+ generic_params
+ .iter_id()
+ .map(|id| {
+ let val = match id {
+ Either::Left(_) => {
+ GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
+ }
+ Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+ };
+ crate::make_binders(db, &generic_params, val)
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
defaults
}
@@ -1602,11 +1660,11 @@ pub(crate) fn generic_defaults_recover(
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_params = TyLoweringContext::new(db, &resolver)
+ let ctx_params = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::Variable);
- let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
- let ctx_ret = TyLoweringContext::new(db, &resolver)
+ let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
+ let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1637,8 +1695,8 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
let data = db.const_data(def);
let generics = generics(db.upcast(), def.into());
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
}
@@ -1647,7 +1705,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
let data = db.static_data(def);
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into());
Binders::empty(Interner, ctx.lower_ty(&data.type_ref))
}
@@ -1656,8 +1714,8 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
let struct_data = db.struct_data(def);
let fields = struct_data.variant_data.fields();
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, AdtId::from(def).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1669,7 +1727,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
if let StructKind::Unit = struct_data.variant_data.kind() {
return type_for_adt(db, def.into());
}
- let generics = generics(db.upcast(), def.into());
+ let generics = generics(db.upcast(), AdtId::from(def).into());
let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
make_binders(
db,
@@ -1683,8 +1741,8 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
let var_data = &enum_data.variants[def.local_id];
let fields = var_data.variant_data.fields();
let resolver = def.parent.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1716,8 +1774,8 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, t.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
if db.type_alias_data(t).is_extern {
Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
} else {
@@ -1838,8 +1896,8 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
"impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
));
let generics = generics(db.upcast(), impl_id.into());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
}
@@ -1848,7 +1906,7 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T
let parent_data = db.generic_params(def.parent());
let data = &parent_data.type_or_consts[def.local_id()];
let resolver = def.parent().resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver);
+ let ctx = TyLoweringContext::new(db, &resolver, def.parent().into());
match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
@@ -1874,8 +1932,8 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
let _cx = stdx::panic_context::enter(format!(
"impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
));
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
let target_trait = impl_data.target_trait.as_ref()?;
Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
@@ -1888,7 +1946,7 @@ pub(crate) fn return_type_impl_traits(
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_ret = TyLoweringContext::new(db, &resolver)
+ let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let _ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1923,7 +1981,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
arg: &'a GenericArg,
this: &mut T,
for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
- for_const: impl FnOnce(&mut T, &ConstRefOrPath, Ty) -> Const + 'a,
+ for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
) -> Option<crate::GenericArg> {
let kind = match kind_id {
Either::Left(_) => ParamKind::Type,
@@ -1948,10 +2006,10 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
// as types. Maybe here is not the best place to do it, but
// it works.
if let TypeRef::Path(p) = t {
- let p = p.mod_path();
+ let p = p.mod_path()?;
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
- let c = ConstRefOrPath::Path(n.clone());
+ let c = ConstRef::Path(n.clone());
return Some(
GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
);
@@ -1967,18 +2025,47 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
pub(crate) fn const_or_path_to_chalk(
db: &dyn HirDatabase,
resolver: &Resolver,
+ owner: TypeOwnerId,
expected_ty: Ty,
- value: &ConstRefOrPath,
+ value: &ConstRef,
mode: ParamLoweringMode,
args: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
) -> Const {
match value {
- ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
- ConstRefOrPath::Path(n) => {
+ ConstRef::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
+ ConstRef::Path(n) => {
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
- path_to_const(db, resolver, &path, mode, args, debruijn)
- .unwrap_or_else(|| unknown_const(expected_ty))
+ path_to_const(
+ db,
+ resolver,
+ &Path::from_known_path_with_no_generic(path),
+ mode,
+ args,
+ debruijn,
+ expected_ty.clone(),
+ )
+ .unwrap_or_else(|| unknown_const(expected_ty))
+ }
+ &ConstRef::Complex(it) => {
+ let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()];
+ if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
+ {
+ // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate
+ // that are unlikely to be edited.
+ return unknown_const(expected_ty);
+ }
+ let c = db
+ .intern_in_type_const(InTypeConstLoc {
+ id: it,
+ owner,
+ thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
+ })
+ .into();
+ intern_const_scalar(
+ ConstScalar::UnevaluatedConst(c, Substitution::empty(Interner)),
+ expected_ty,
+ )
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index f3a27632b..ab6430e8f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -2,25 +2,28 @@
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
-use std::{ops::ControlFlow, sync::Arc};
+use std::ops::ControlFlow;
use base_db::{CrateId, Edition};
-use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex};
+use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{
- data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
- BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
- ModuleId, TraitId,
+ data::{adt::StructFlags, ImplData},
+ item_scope::ItemScope,
+ nameres::DefMap,
+ AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
+ ModuleDefId, ModuleId, TraitId,
};
use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use stdx::never;
+use triomphe::Arc;
use crate::{
autoderef::{self, AutoderefKind},
db::HirDatabase,
from_chalk_trait_id, from_foreign_def_id,
- infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
+ infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
@@ -147,31 +150,30 @@ impl TraitImpls {
Arc::new(impls)
}
- pub(crate) fn trait_impls_in_block_query(
- db: &dyn HirDatabase,
- block: BlockId,
- ) -> Option<Arc<Self>> {
+ pub(crate) fn trait_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> {
let _p = profile::span("trait_impls_in_block_query");
let mut impls = Self { map: FxHashMap::default() };
- let block_def_map = db.block_def_map(block)?;
+ let block_def_map = db.block_def_map(block);
impls.collect_def_map(db, &block_def_map);
impls.shrink_to_fit();
- Some(Arc::new(impls))
+ Arc::new(impls)
}
- pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+ pub(crate) fn trait_impls_in_deps_query(
+ db: &dyn HirDatabase,
+ krate: CrateId,
+ ) -> Arc<[Arc<Self>]> {
let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}"));
let crate_graph = db.crate_graph();
- let mut res = Self { map: FxHashMap::default() };
-
- for krate in crate_graph.transitive_deps(krate) {
- res.merge(&db.trait_impls_in_crate(krate));
- }
- res.shrink_to_fit();
-
- Arc::new(res)
+ // FIXME: use `Arc::from_iter` when it becomes available
+ Arc::from(
+ crate_graph
+ .transitive_deps(krate)
+ .map(|krate| db.trait_impls_in_crate(krate))
+ .collect::<Vec<_>>(),
+ )
}
fn shrink_to_fit(&mut self) {
@@ -185,6 +187,15 @@ impl TraitImpls {
fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
+ // Reservation impls should be ignored during trait resolution, so we never need
+ // them during type analysis. See rust-lang/rust#64631 for details.
+ //
+ // FIXME: Reservation impls should be considered during coherence checks. If we are
+ // (ever) to implement coherence checks, this filtering should be done by the trait
+ // solver.
+ if db.attrs(impl_id.into()).by_key("rustc_reservation_impl").exists() {
+ continue;
+ }
let target_trait = match db.impl_trait(impl_id) {
Some(tr) => tr.skip_binders().hir_trait_id(),
None => continue,
@@ -210,15 +221,6 @@ impl TraitImpls {
}
}
- fn merge(&mut self, other: &Self) {
- for (trait_, other_map) in &other.map {
- let map = self.map.entry(*trait_).or_default();
- for (fp, impls) in other_map {
- map.entry(*fp).or_default().extend(impls);
- }
- }
- }
-
/// Queries all trait impls for the given type.
pub fn for_self_ty_without_blanket_impls(
&self,
@@ -271,6 +273,7 @@ pub struct InherentImpls {
impl InherentImpls {
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+ let _p = profile::span("inherent_impls_in_crate_query").detail(|| format!("{krate:?}"));
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
let crate_def_map = db.crate_def_map(krate);
@@ -280,17 +283,15 @@ impl InherentImpls {
Arc::new(impls)
}
- pub(crate) fn inherent_impls_in_block_query(
- db: &dyn HirDatabase,
- block: BlockId,
- ) -> Option<Arc<Self>> {
+ pub(crate) fn inherent_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> {
+ let _p = profile::span("inherent_impls_in_block_query");
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
- if let Some(block_def_map) = db.block_def_map(block) {
- impls.collect_def_map(db, &block_def_map);
- impls.shrink_to_fit();
- return Some(Arc::new(impls));
- }
- None
+
+ let block_def_map = db.block_def_map(block);
+ impls.collect_def_map(db, &block_def_map);
+ impls.shrink_to_fit();
+
+ Arc::new(impls)
}
fn shrink_to_fit(&mut self) {
@@ -404,12 +405,14 @@ pub fn def_crates(
match ty.kind(Interner) {
&TyKind::Adt(AdtId(def_id), _) => {
let rustc_has_incoherent_inherent_impls = match def_id {
- hir_def::AdtId::StructId(id) => {
- db.struct_data(id).rustc_has_incoherent_inherent_impls
- }
- hir_def::AdtId::UnionId(id) => {
- db.union_data(id).rustc_has_incoherent_inherent_impls
- }
+ hir_def::AdtId::StructId(id) => db
+ .struct_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
+ hir_def::AdtId::UnionId(id) => db
+ .union_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
};
Some(if rustc_has_incoherent_inherent_impls {
@@ -449,55 +452,6 @@ pub fn def_crates(
}
}
-pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
- use hir_expand::name;
- use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
- Some(match op {
- BinaryOp::LogicOp(_) => return None,
- BinaryOp::ArithOp(aop) => match aop {
- ArithOp::Add => (name![add], LangItem::Add),
- ArithOp::Mul => (name![mul], LangItem::Mul),
- ArithOp::Sub => (name![sub], LangItem::Sub),
- ArithOp::Div => (name![div], LangItem::Div),
- ArithOp::Rem => (name![rem], LangItem::Rem),
- ArithOp::Shl => (name![shl], LangItem::Shl),
- ArithOp::Shr => (name![shr], LangItem::Shr),
- ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
- ArithOp::BitOr => (name![bitor], LangItem::BitOr),
- ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
- },
- BinaryOp::Assignment { op: Some(aop) } => match aop {
- ArithOp::Add => (name![add_assign], LangItem::AddAssign),
- ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
- ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
- ArithOp::Div => (name![div_assign], LangItem::DivAssign),
- ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
- ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
- ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
- ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
- ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
- ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
- },
- BinaryOp::CmpOp(cop) => match cop {
- CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
- CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
- CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
- (name![le], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
- (name![lt], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
- (name![ge], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
- (name![gt], LangItem::PartialOrd)
- }
- },
- BinaryOp::Assignment { op: None } => return None,
- })
-}
-
/// Look up the method with the given name.
pub(crate) fn lookup_method(
db: &dyn HirDatabase,
@@ -600,9 +554,9 @@ impl ReceiverAdjustments {
}
}
if let Some(m) = self.autoref {
- ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
- adjust
- .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
+ let a = Adjustment::borrow(m, ty);
+ ty = a.target.clone();
+ adjust.push(a);
}
if self.unsize_array {
ty = 'x: {
@@ -616,7 +570,7 @@ impl ReceiverAdjustments {
.intern(Interner);
}
}
- never!("unsize_array with non-reference-to-array {:?}", ty);
+ // FIXME: report diagnostic if array unsizing happens without indirection.
ty
};
adjust.push(Adjustment {
@@ -692,6 +646,39 @@ pub fn lookup_impl_const(
.unwrap_or((const_id, subs))
}
+/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
+/// call the method using the vtable.
+pub fn is_dyn_method(
+ db: &dyn HirDatabase,
+ _env: Arc<TraitEnvironment>,
+ func: FunctionId,
+ fn_subst: Substitution,
+) -> Option<usize> {
+ let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
+ return None;
+ };
+ let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
+ let fn_params = fn_subst.len(Interner) - trait_params;
+ let trait_ref = TraitRef {
+ trait_id: to_chalk_trait_id(trait_id),
+ substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
+ };
+ let self_ty = trait_ref.self_type_parameter(Interner);
+ if let TyKind::Dyn(d) = self_ty.kind(Interner) {
+ let is_my_trait_in_bounds =
+ d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() {
+ // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
+ // what the generics are, we are sure that the method is come from the vtable.
+ WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id,
+ _ => false,
+ });
+ if is_my_trait_in_bounds {
+ return Some(fn_params);
+ }
+ }
+ None
+}
+
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
@@ -701,9 +688,8 @@ pub fn lookup_impl_method(
func: FunctionId,
fn_subst: Substitution,
) -> (FunctionId, Substitution) {
- let trait_id = match func.lookup(db.upcast()).container {
- ItemContainerId::TraitId(id) => id,
- _ => return (func, fn_subst),
+ let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
+ return (func, fn_subst)
};
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params;
@@ -713,7 +699,7 @@ pub fn lookup_impl_method(
};
let name = &db.function_data(func).name;
- lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
+ let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc {
Some((id, subst))
@@ -721,7 +707,16 @@ pub fn lookup_impl_method(
None
}
})
- .unwrap_or((func, fn_subst))
+ else {
+ return (func, fn_subst);
+ };
+ (
+ impl_fn,
+ Substitution::from_iter(
+ Interner,
+ fn_subst.iter(Interner).take(fn_params).chain(impl_subst.iter(Interner)),
+ ),
+ )
}
fn lookup_impl_assoc_item_for_trait_ref(
@@ -730,10 +725,20 @@ fn lookup_impl_assoc_item_for_trait_ref(
env: Arc<TraitEnvironment>,
name: &Name,
) -> Option<(AssocItemId, Substitution)> {
+ let hir_trait_id = trait_ref.hir_trait_id();
let self_ty = trait_ref.self_type_parameter(Interner);
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
let impls = db.trait_impls_in_deps(env.krate);
- let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp);
+ let self_impls = match self_ty.kind(Interner) {
+ TyKind::Adt(id, _) => {
+ id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x))
+ }
+ _ => None,
+ };
+ let impls = impls
+ .iter()
+ .chain(self_impls.as_ref())
+ .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
let table = InferenceTable::new(db, env);
@@ -759,9 +764,8 @@ fn find_matching_impl(
actual_trait_ref: TraitRef,
) -> Option<(Arc<ImplData>, Substitution)> {
let db = table.db;
- loop {
- let impl_ = impls.next()?;
- let r = table.run_in_snapshot(|table| {
+ impls.find_map(|impl_| {
+ table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
let impl_substs =
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
@@ -778,12 +782,11 @@ fn find_matching_impl(
.into_iter()
.map(|b| b.cast(Interner));
let goal = crate::Goal::all(Interner, wcs);
- table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
- });
- if r.is_some() {
- break r;
- }
- }
+ table.try_obligation(goal.clone())?;
+ table.register_obligation(goal);
+ Some((impl_data, table.resolve_completely(impl_substs)))
+ })
+ })
}
fn is_inherent_impl_coherent(
@@ -824,12 +827,14 @@ fn is_inherent_impl_coherent(
| TyKind::Scalar(_) => true,
&TyKind::Adt(AdtId(adt), _) => match adt {
- hir_def::AdtId::StructId(it) => {
- db.struct_data(it).rustc_has_incoherent_inherent_impls
- }
- hir_def::AdtId::UnionId(it) => {
- db.union_data(it).rustc_has_incoherent_inherent_impls
- }
+ hir_def::AdtId::StructId(id) => db
+ .struct_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
+ hir_def::AdtId::UnionId(id) => db
+ .union_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
},
TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
@@ -963,7 +968,14 @@ fn iterate_method_candidates_with_autoref(
)
};
- iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
+ let mut maybe_reborrowed = first_adjustment.clone();
+ if let Some((_, _, m)) = receiver_ty.value.as_reference() {
+ // Prefer reborrow of references to move
+ maybe_reborrowed.autoref = Some(m);
+ maybe_reborrowed.autoderefs += 1;
+ }
+
+ iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
let refed = Canonical {
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
@@ -1108,7 +1120,7 @@ fn iterate_trait_method_candidates(
};
if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
- if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
+ if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() {
continue 'traits;
}
}
@@ -1180,23 +1192,19 @@ fn iterate_inherent_methods(
};
while let Some(block_id) = block {
- if let Some(impls) = db.inherent_impls_in_block(block_id) {
- impls_for_self_ty(
- &impls,
- self_ty,
- table,
- name,
- receiver_ty,
- receiver_adjustments.clone(),
- module,
- callback,
- )?;
- }
+ let impls = db.inherent_impls_in_block(block_id);
+ impls_for_self_ty(
+ &impls,
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ module,
+ callback,
+ )?;
- block = db
- .block_def_map(block_id)
- .and_then(|map| map.parent())
- .and_then(|module| module.containing_block());
+ block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block());
}
for krate in def_crates {
@@ -1274,7 +1282,7 @@ fn iterate_inherent_methods(
}
/// Returns the receiver type for the index trait call.
-pub fn resolve_indexing_op(
+pub(crate) fn resolve_indexing_op(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
ty: Canonical<Ty>,
@@ -1284,8 +1292,11 @@ pub fn resolve_indexing_op(
let ty = table.instantiate_canonical(ty);
let deref_chain = autoderef_method_receiver(&mut table, ty);
for (ty, adj) in deref_chain {
- let goal = generic_implements_goal(db, env.clone(), index_trait, &ty);
- if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
+ let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty);
+ if db
+ .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner))
+ .is_some()
+ {
return Some(adj);
}
}
@@ -1310,14 +1321,12 @@ fn is_valid_candidate(
) -> IsValidCandidate {
let db = table.db;
match item {
- AssocItemId::FunctionId(m) => {
- is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
+ AssocItemId::FunctionId(f) => {
+ is_valid_fn_candidate(table, f, name, receiver_ty, self_ty, visible_from_module)
}
AssocItemId::ConstId(c) => {
- let data = db.const_data(c);
check_that!(receiver_ty.is_none());
-
- check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
+ check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n)));
if let Some(from_module) = visible_from_module {
if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
@@ -1441,7 +1450,7 @@ pub fn implements_trait(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
- let solution = db.trait_solve(env.krate, goal.cast(Interner));
+ let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
solution.is_some()
}
@@ -1453,7 +1462,7 @@ pub fn implements_trait_unique(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
- let solution = db.trait_solve(env.krate, goal.cast(Interner));
+ let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
matches!(solution, Some(crate::Solution::Unique(_)))
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 7c1cbbdf5..2345bab0b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -3,12 +3,15 @@
use std::{fmt::Display, iter};
use crate::{
- infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
+ consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast,
+ lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar,
+ InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind,
};
+use base_db::CrateId;
use chalk_ir::Mutability;
use hir_def::{
- expr::{BindingId, Expr, ExprId, Ordering, PatId},
- DefWithBodyId, FieldId, UnionId, VariantId,
+ hir::{BindingId, Expr, ExprId, Ordering, PatId},
+ DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@@ -16,12 +19,19 @@ mod eval;
mod lower;
mod borrowck;
mod pretty;
+mod monomorphization;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
-pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
-pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
+pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
+pub use lower::{
+ lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
+};
+pub use monomorphization::{
+ monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
+ monomorphized_mir_body_query, monomorphized_mir_body_recover,
+};
use smallvec::{smallvec, SmallVec};
-use stdx::impl_from;
+use stdx::{impl_from, never};
use super::consteval::{intern_const_scalar, try_const_usize};
@@ -32,7 +42,7 @@ fn return_slot() -> LocalId {
LocalId::from_raw(RawIdx::from(0))
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Local {
pub ty: Ty,
}
@@ -52,7 +62,7 @@ pub struct Local {
/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
/// something we can even decide without knowing more about Rust's memory model?
///
-/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri
+/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
/// currently implements it, but it seems like this may be something to check against in the
/// validator.
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -73,6 +83,9 @@ pub enum Operand {
Move(Place),
/// Constants are already semantically values, and remain unchanged.
Constant(Const),
+ /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
+ /// handles it with the `Constant` variant somehow.
+ Static(StaticId),
}
impl Operand {
@@ -87,31 +100,141 @@ impl Operand {
fn const_zst(ty: Ty) -> Operand {
Self::from_bytes(vec![], ty)
}
+
+ fn from_fn(
+ db: &dyn HirDatabase,
+ func_id: hir_def::FunctionId,
+ generic_args: Substitution,
+ ) -> Operand {
+ let ty =
+ chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
+ .intern(Interner);
+ Operand::from_bytes(vec![], ty)
+ }
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectionElem<V, T> {
Deref,
Field(FieldId),
- TupleField(usize),
+ // FIXME: get rid of this, and use FieldId for tuples and closures
+ TupleOrClosureField(usize),
Index(V),
- ConstantIndex { offset: u64, min_length: u64, from_end: bool },
- Subslice { from: u64, to: u64, from_end: bool },
+ ConstantIndex { offset: u64, from_end: bool },
+ Subslice { from: u64, to: u64 },
//Downcast(Option<Symbol>, VariantIdx),
OpaqueCast(T),
}
+impl<V, T> ProjectionElem<V, T> {
+ pub fn projected_ty(
+ &self,
+ base: Ty,
+ db: &dyn HirDatabase,
+ closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
+ krate: CrateId,
+ ) -> Ty {
+ match self {
+ ProjectionElem::Deref => match &base.data(Interner).kind {
+ TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
+ TyKind::Adt(adt, subst) if is_box(db, adt.0) => {
+ subst.at(Interner, 0).assert_ty_ref(Interner).clone()
+ }
+ _ => {
+ never!("Overloaded deref on type {} is not a projection", base.display(db));
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::Field(f) => match &base.data(Interner).kind {
+ TyKind::Adt(_, subst) => {
+ db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
+ }
+ _ => {
+ never!("Only adt has field");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind {
+ TyKind::Tuple(_, subst) => subst
+ .as_slice(Interner)
+ .get(*f)
+ .map(|x| x.assert_ty_ref(Interner))
+ .cloned()
+ .unwrap_or_else(|| {
+ never!("Out of bound tuple field");
+ TyKind::Error.intern(Interner)
+ }),
+ TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
+ _ => {
+ never!("Only tuple or closure has tuple or closure field");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => {
+ match &base.data(Interner).kind {
+ TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
+ _ => {
+ never!("Overloaded index is not a projection");
+ return TyKind::Error.intern(Interner);
+ }
+ }
+ }
+ &ProjectionElem::Subslice { from, to } => match &base.data(Interner).kind {
+ TyKind::Array(inner, c) => {
+ let next_c = usize_const(
+ db,
+ match try_const_usize(db, c) {
+ None => None,
+ Some(x) => x.checked_sub(u128::from(from + to)),
+ },
+ krate,
+ );
+ TyKind::Array(inner.clone(), next_c).intern(Interner)
+ }
+ TyKind::Slice(_) => base.clone(),
+ _ => {
+ never!("Subslice projection should only happen on slice and array");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::OpaqueCast(_) => {
+ never!("We don't emit these yet");
+ return TyKind::Error.intern(Interner);
+ }
+ }
+ }
+}
+
type PlaceElem = ProjectionElem<LocalId, Ty>;
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Place {
pub local: LocalId,
- pub projection: Vec<PlaceElem>,
+ pub projection: Box<[PlaceElem]>,
+}
+
+impl Place {
+ fn is_parent(&self, child: &Place) -> bool {
+ self.local == child.local && child.projection.starts_with(&self.projection)
+ }
+
+ fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
+ (0..self.projection.len())
+ .map(|x| &self.projection[0..x])
+ .map(|x| Place { local: self.local, projection: x.to_vec().into() })
+ }
+
+ fn project(&self, projection: PlaceElem) -> Place {
+ Place {
+ local: self.local,
+ projection: self.projection.iter().cloned().chain([projection]).collect(),
+ }
+ }
}
impl From<LocalId> for Place {
fn from(local: LocalId) -> Self {
- Self { local, projection: vec![] }
+ Self { local, projection: vec![].into() }
}
}
@@ -123,7 +246,7 @@ pub enum AggregateKind {
Tuple(Ty),
Adt(VariantId, Substitution),
Union(UnionId, FieldId),
- //Closure(LocalDefId, SubstsRef),
+ Closure(Ty),
//Generator(LocalDefId, SubstsRef, Movability),
}
@@ -197,7 +320,13 @@ impl SwitchTargets {
}
#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Terminator {
+pub struct Terminator {
+ span: MirSpan,
+ kind: TerminatorKind,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum TerminatorKind {
/// Block has one successor; we continue execution there.
Goto { target: BasicBlockId },
@@ -320,7 +449,7 @@ pub enum Terminator {
/// These are owned by the callee, which is free to modify them.
/// This allows the memory occupied by "by-value" arguments to be
/// reused across function calls without duplicating the contents.
- args: Vec<Operand>,
+ args: Box<[Operand]>,
/// Where the returned value will be written
destination: Place,
/// Where to go after this call returns. If none, the call necessarily diverges.
@@ -418,7 +547,7 @@ pub enum Terminator {
},
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
@@ -564,6 +693,20 @@ pub enum BinOp {
Offset,
}
+impl BinOp {
+ fn run_compare<T: PartialEq + PartialOrd>(&self, l: T, r: T) -> bool {
+ match self {
+ BinOp::Ge => l >= r,
+ BinOp::Gt => l > r,
+ BinOp::Le => l <= r,
+ BinOp::Lt => l < r,
+ BinOp::Eq => l == r,
+ BinOp::Ne => l != r,
+ x => panic!("`run_compare` called on operator {x:?}"),
+ }
+ }
+}
+
impl Display for BinOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
@@ -588,32 +731,32 @@ impl Display for BinOp {
}
}
-impl From<hir_def::expr::ArithOp> for BinOp {
- fn from(value: hir_def::expr::ArithOp) -> Self {
+impl From<hir_def::hir::ArithOp> for BinOp {
+ fn from(value: hir_def::hir::ArithOp) -> Self {
match value {
- hir_def::expr::ArithOp::Add => BinOp::Add,
- hir_def::expr::ArithOp::Mul => BinOp::Mul,
- hir_def::expr::ArithOp::Sub => BinOp::Sub,
- hir_def::expr::ArithOp::Div => BinOp::Div,
- hir_def::expr::ArithOp::Rem => BinOp::Rem,
- hir_def::expr::ArithOp::Shl => BinOp::Shl,
- hir_def::expr::ArithOp::Shr => BinOp::Shr,
- hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
- hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
- hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
+ hir_def::hir::ArithOp::Add => BinOp::Add,
+ hir_def::hir::ArithOp::Mul => BinOp::Mul,
+ hir_def::hir::ArithOp::Sub => BinOp::Sub,
+ hir_def::hir::ArithOp::Div => BinOp::Div,
+ hir_def::hir::ArithOp::Rem => BinOp::Rem,
+ hir_def::hir::ArithOp::Shl => BinOp::Shl,
+ hir_def::hir::ArithOp::Shr => BinOp::Shr,
+ hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
+ hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
+ hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
}
}
}
-impl From<hir_def::expr::CmpOp> for BinOp {
- fn from(value: hir_def::expr::CmpOp) -> Self {
+impl From<hir_def::hir::CmpOp> for BinOp {
+ fn from(value: hir_def::hir::CmpOp) -> Self {
match value {
- hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
- hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
+ hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
+ hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
}
}
}
@@ -642,7 +785,6 @@ pub enum CastKind {
FloatToInt,
FloatToFloat,
IntToFloat,
- PtrToPtr,
FnPtrToPtr,
}
@@ -653,13 +795,8 @@ pub enum Rvalue {
/// Creates an array where each element is the value of the operand.
///
- /// This is the cause of a bug in the case where the repetition count is zero because the value
- /// is not dropped, see [#74836].
- ///
/// Corresponds to source code like `[x; 32]`.
- ///
- /// [#74836]: https://github.com/rust-lang/rust/issues/74836
- //Repeat(Operand, ty::Const),
+ Repeat(Operand, Const),
/// Creates a reference of the indicated kind to the place.
///
@@ -768,7 +905,7 @@ pub enum Rvalue {
///
/// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After
/// generator lowering, `Generator` aggregate kinds are disallowed too.
- Aggregate(AggregateKind, Vec<Operand>),
+ Aggregate(AggregateKind, Box<[Operand]>),
/// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
///
@@ -777,6 +914,9 @@ pub enum Rvalue {
/// affects alias analysis.
ShallowInitBox(Operand, Ty),
+ /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
+ ShallowInitBoxWithAlloc(Ty),
+
/// A CopyForDeref is equivalent to a read from a place at the
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
@@ -816,7 +956,7 @@ pub struct Statement {
pub span: MirSpan,
}
-#[derive(Debug, Default, PartialEq, Eq)]
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct BasicBlock {
/// List of statements in this block.
pub statements: Vec<Statement>,
@@ -838,19 +978,118 @@ pub struct BasicBlock {
pub is_cleanup: bool,
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MirBody {
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
pub owner: DefWithBodyId,
- pub arg_count: usize,
pub binding_locals: ArenaMap<BindingId, LocalId>,
pub param_locals: Vec<LocalId>,
+ /// This field stores the closures directly owned by this body. It is used
+ /// in traversing every mir body.
+ pub closures: Vec<ClosureId>,
}
-fn const_as_usize(c: &Const) -> usize {
- try_const_usize(c).unwrap() as usize
+impl MirBody {
+ fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
+ fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+ match op {
+ Operand::Copy(p) | Operand::Move(p) => {
+ f(p);
+ }
+ Operand::Constant(_) | Operand::Static(_) => (),
+ }
+ }
+ for (_, block) in self.basic_blocks.iter_mut() {
+ for statement in &mut block.statements {
+ match &mut statement.kind {
+ StatementKind::Assign(p, r) => {
+ f(p);
+ match r {
+ Rvalue::ShallowInitBoxWithAlloc(_) => (),
+ Rvalue::ShallowInitBox(o, _)
+ | Rvalue::UnaryOp(_, o)
+ | Rvalue::Cast(_, o, _)
+ | Rvalue::Repeat(o, _)
+ | Rvalue::Use(o) => for_operand(o, &mut f),
+ Rvalue::CopyForDeref(p)
+ | Rvalue::Discriminant(p)
+ | Rvalue::Len(p)
+ | Rvalue::Ref(_, p) => f(p),
+ Rvalue::CheckedBinaryOp(_, o1, o2) => {
+ for_operand(o1, &mut f);
+ for_operand(o2, &mut f);
+ }
+ Rvalue::Aggregate(_, ops) => {
+ for op in ops.iter_mut() {
+ for_operand(op, &mut f);
+ }
+ }
+ }
+ }
+ StatementKind::Deinit(p) => f(p),
+ StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ match &mut block.terminator {
+ Some(x) => match &mut x.kind {
+ TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+ TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable => (),
+ TerminatorKind::Drop { place, .. } => {
+ f(place);
+ }
+ TerminatorKind::DropAndReplace { place, value, .. } => {
+ f(place);
+ for_operand(value, &mut f);
+ }
+ TerminatorKind::Call { func, args, destination, .. } => {
+ for_operand(func, &mut f);
+ args.iter_mut().for_each(|x| for_operand(x, &mut f));
+ f(destination);
+ }
+ TerminatorKind::Assert { cond, .. } => {
+ for_operand(cond, &mut f);
+ }
+ TerminatorKind::Yield { value, resume_arg, .. } => {
+ for_operand(value, &mut f);
+ f(resume_arg);
+ }
+ },
+ None => (),
+ }
+ }
+ }
+
+ fn shrink_to_fit(&mut self) {
+ let MirBody {
+ basic_blocks,
+ locals,
+ start_block: _,
+ owner: _,
+ binding_locals,
+ param_locals,
+ closures,
+ } = self;
+ basic_blocks.shrink_to_fit();
+ locals.shrink_to_fit();
+ binding_locals.shrink_to_fit();
+ param_locals.shrink_to_fit();
+ closures.shrink_to_fit();
+ for (_, b) in basic_blocks.iter_mut() {
+ let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
+ statements.shrink_to_fit();
+ }
+ }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index c8729af86..a5dd0182e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -3,17 +3,20 @@
// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
// if needed for implementing a proper borrow checker.
-use std::sync::Arc;
+use std::iter;
-use hir_def::DefWithBodyId;
+use hir_def::{DefWithBodyId, HasModule};
use la_arena::ArenaMap;
use stdx::never;
+use triomphe::Arc;
-use crate::db::HirDatabase;
+use crate::{
+ db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt, TypeFlags,
+};
use super::{
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
- Rvalue, StatementKind, Terminator,
+ Rvalue, StatementKind, TerminatorKind,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -24,25 +27,166 @@ pub enum MutabilityReason {
}
#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MovedOutOfRef {
+ pub ty: Ty,
+ pub span: MirSpan,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BorrowckResult {
pub mir_body: Arc<MirBody>,
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
+ pub moved_out_of_ref: Vec<MovedOutOfRef>,
+}
+
+fn all_mir_bodies(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ fn for_closure(
+ db: &dyn HirDatabase,
+ c: ClosureId,
+ ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ match db.mir_body_for_closure(c) {
+ Ok(body) => {
+ let closures = body.closures.clone();
+ Box::new(
+ iter::once(Ok(body))
+ .chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+ )
+ }
+ Err(e) => Box::new(iter::once(Err(e))),
+ }
+ }
+ match db.mir_body(def) {
+ Ok(body) => {
+ let closures = body.closures.clone();
+ Box::new(
+ iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+ )
+ }
+ Err(e) => Box::new(iter::once(Err(e))),
+ }
}
pub fn borrowck_query(
db: &dyn HirDatabase,
def: DefWithBodyId,
-) -> Result<Arc<BorrowckResult>, MirLowerError> {
+) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query");
- let body = db.mir_body(def)?;
- let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
- Ok(Arc::new(r))
+ let r = all_mir_bodies(db, def)
+ .map(|body| {
+ let body = body?;
+ Ok(BorrowckResult {
+ mutability_of_locals: mutability_of_locals(db, &body),
+ moved_out_of_ref: moved_out_of_ref(db, &body),
+ mir_body: body,
+ })
+ })
+ .collect::<Result<Vec<_>, MirLowerError>>()?;
+ Ok(r.into())
}
-fn is_place_direct(lvalue: &Place) -> bool {
- !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
+fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
+ let mut result = vec![];
+ let mut for_operand = |op: &Operand, span: MirSpan| match op {
+ Operand::Copy(p) | Operand::Move(p) => {
+ let mut ty: Ty = body.locals[p.local].ty.clone();
+ let mut is_dereference_of_ref = false;
+ for proj in &*p.projection {
+ if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
+ is_dereference_of_ref = true;
+ }
+ ty = proj.projected_ty(
+ ty,
+ db,
+ |c, subst, f| {
+ let (def, _) = db.lookup_intern_closure(c.into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ body.owner.module(db.upcast()).krate(),
+ );
+ }
+ if is_dereference_of_ref
+ && !ty.clone().is_copy(db, body.owner)
+ && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
+ {
+ result.push(MovedOutOfRef { span, ty });
+ }
+ }
+ Operand::Constant(_) | Operand::Static(_) => (),
+ };
+ for (_, block) in body.basic_blocks.iter() {
+ for statement in &block.statements {
+ match &statement.kind {
+ StatementKind::Assign(_, r) => match r {
+ Rvalue::ShallowInitBoxWithAlloc(_) => (),
+ Rvalue::ShallowInitBox(o, _)
+ | Rvalue::UnaryOp(_, o)
+ | Rvalue::Cast(_, o, _)
+ | Rvalue::Repeat(o, _)
+ | Rvalue::Use(o) => for_operand(o, statement.span),
+ Rvalue::CopyForDeref(_)
+ | Rvalue::Discriminant(_)
+ | Rvalue::Len(_)
+ | Rvalue::Ref(_, _) => (),
+ Rvalue::CheckedBinaryOp(_, o1, o2) => {
+ for_operand(o1, statement.span);
+ for_operand(o2, statement.span);
+ }
+ Rvalue::Aggregate(_, ops) => {
+ for op in ops.iter() {
+ for_operand(op, statement.span);
+ }
+ }
+ },
+ StatementKind::Deinit(_)
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ match &block.terminator {
+ Some(terminator) => match &terminator.kind {
+ TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
+ TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. } => (),
+ TerminatorKind::DropAndReplace { value, .. } => {
+ for_operand(value, terminator.span);
+ }
+ TerminatorKind::Call { func, args, .. } => {
+ for_operand(func, terminator.span);
+ args.iter().for_each(|x| for_operand(x, terminator.span));
+ }
+ TerminatorKind::Assert { cond, .. } => {
+ for_operand(cond, terminator.span);
+ }
+ TerminatorKind::Yield { value, .. } => {
+ for_operand(value, terminator.span);
+ }
+ },
+ None => (),
+ }
+ }
+ result
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ProjectionCase {
/// Projection is a local
Direct,
@@ -52,20 +196,39 @@ enum ProjectionCase {
Indirect,
}
-fn place_case(lvalue: &Place) -> ProjectionCase {
+fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
let mut is_part_of = false;
- for proj in lvalue.projection.iter().rev() {
+ let mut ty = body.locals[lvalue.local].ty.clone();
+ for proj in lvalue.projection.iter() {
match proj {
- ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect
- ProjectionElem::ConstantIndex { .. }
+ ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
+ ProjectionElem::Deref // It's direct in case of `Box<T>`
+ | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_)
- | ProjectionElem::TupleField(_)
+ | ProjectionElem::TupleOrClosureField(_)
| ProjectionElem::Index(_) => {
is_part_of = true;
}
ProjectionElem::OpaqueCast(_) => (),
}
+ ty = proj.projected_ty(
+ ty,
+ db,
+ |c, subst, f| {
+ let (def, _) = db.lookup_intern_closure(c.into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ body.owner.module(db.upcast()).krate(),
+ );
}
if is_part_of {
ProjectionCase::DirectPart
@@ -76,11 +239,15 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
/// Returns a map from basic blocks to the set of locals that might be ever initialized before
/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
-/// `Uninit` and `drop` and similars after initialization.
-fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
+/// `Uninit` and `drop` and similar after initialization.
+fn ever_initialized_map(
+ db: &dyn HirDatabase,
+ body: &MirBody,
+) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
fn dfs(
+ db: &dyn HirDatabase,
body: &MirBody,
b: BasicBlockId,
l: LocalId,
@@ -104,29 +271,31 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
}
}
let Some(terminator) = &block.terminator else {
- never!("Terminator should be none only in construction");
+ never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db));
return;
};
- let targets = match terminator {
- Terminator::Goto { target } => vec![*target],
- Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
- Terminator::Resume
- | Terminator::Abort
- | Terminator::Return
- | Terminator::Unreachable => vec![],
- Terminator::Call { target, cleanup, destination, .. } => {
+ let targets = match &terminator.kind {
+ TerminatorKind::Goto { target } => vec![*target],
+ TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
+ TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable => vec![],
+ TerminatorKind::Call { target, cleanup, destination, .. } => {
if destination.projection.len() == 0 && destination.local == l {
is_ever_initialized = true;
}
target.into_iter().chain(cleanup.into_iter()).copied().collect()
}
- Terminator::Drop { .. }
- | Terminator::DropAndReplace { .. }
- | Terminator::Assert { .. }
- | Terminator::Yield { .. }
- | Terminator::GeneratorDrop
- | Terminator::FalseEdge { .. }
- | Terminator::FalseUnwind { .. } => {
+ TerminatorKind::Drop { target, unwind, place: _ } => {
+ Some(target).into_iter().chain(unwind.into_iter()).copied().collect()
+ }
+ TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet");
vec![]
}
@@ -134,37 +303,40 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
for target in targets {
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
result[target].insert(l, is_ever_initialized);
- dfs(body, target, l, result);
+ dfs(db, body, target, l, result);
}
}
}
for &l in &body.param_locals {
result[body.start_block].insert(l, true);
- dfs(body, body.start_block, l, &mut result);
+ dfs(db, body, body.start_block, l, &mut result);
}
for l in body.locals.iter().map(|x| x.0) {
if !result[body.start_block].contains_idx(l) {
result[body.start_block].insert(l, false);
- dfs(body, body.start_block, l, &mut result);
+ dfs(db, body, body.start_block, l, &mut result);
}
}
result
}
-fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
+fn mutability_of_locals(
+ db: &dyn HirDatabase,
+ body: &MirBody,
+) -> ArenaMap<LocalId, MutabilityReason> {
let mut result: ArenaMap<LocalId, MutabilityReason> =
body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
let mut push_mut_span = |local, span| match &mut result[local] {
MutabilityReason::Mut { spans } => spans.push(span),
x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
};
- let ever_init_maps = ever_initialized_map(body);
+ let ever_init_maps = ever_initialized_map(db, body);
for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
let block = &body.basic_blocks[block_id];
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(place, value) => {
- match place_case(place) {
+ match place_case(db, body, place) {
ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() {
push_mut_span(place.local, statement.span);
@@ -179,7 +351,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
ProjectionCase::Indirect => (),
}
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
- if is_place_direct(p) {
+ if place_case(db, body, p) != ProjectionCase::Indirect {
push_mut_span(p.local, statement.span);
}
}
@@ -194,21 +366,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
never!("Terminator should be none only in construction");
continue;
};
- match terminator {
- Terminator::Goto { .. }
- | Terminator::Resume
- | Terminator::Abort
- | Terminator::Return
- | Terminator::Unreachable
- | Terminator::FalseEdge { .. }
- | Terminator::FalseUnwind { .. }
- | Terminator::GeneratorDrop
- | Terminator::SwitchInt { .. }
- | Terminator::Drop { .. }
- | Terminator::DropAndReplace { .. }
- | Terminator::Assert { .. }
- | Terminator::Yield { .. } => (),
- Terminator::Call { destination, .. } => {
+ match &terminator.kind {
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::SwitchInt { .. }
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. } => (),
+ TerminatorKind::Call { destination, .. } => {
if destination.projection.len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
push_mut_span(destination.local, MirSpan::Unknown);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index c5d843d9e..9acf9d39e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -1,41 +1,134 @@
//! This module provides a MIR interpreter, which is used in const eval.
-use std::{borrow::Cow, collections::HashMap, iter};
+use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range};
-use base_db::CrateId;
-use chalk_ir::{
- fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
- DebruijnIndex, TyKind,
-};
+use base_db::{CrateId, FileId};
+use chalk_ir::Mutability;
+use either::Either;
use hir_def::{
builtin_type::BuiltinType,
+ data::adt::{StructFlags, VariantData},
lang_item::{lang_attr, LangItem},
- layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
- AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+ layout::{TagEncoding, Variants},
+ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
+ VariantId,
};
+use hir_expand::InFile;
use intern::Interned;
use la_arena::ArenaMap;
+use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::never;
+use syntax::{SyntaxNodePtr, TextRange};
+use triomphe::Arc;
use crate::{
- consteval::{intern_const_scalar, ConstEvalError},
+ consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
db::HirDatabase,
- from_placeholder_idx,
- infer::{normalize, PointerCast},
- layout::layout_of_ty,
+ display::{ClosureStyle, HirDisplay},
+ infer::PointerCast,
+ layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk,
- method_resolution::lookup_impl_method,
- CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
+ method_resolution::{is_dyn_method, lookup_impl_method},
+ name, static_lifetime,
+ traits::FnTrait,
+ utils::{detect_variant_from_bytes, ClosureSubst},
+ CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
+ Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
- const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError,
- Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
+ return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand,
+ Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp,
};
+mod shim;
+#[cfg(test)]
+mod tests;
+
+macro_rules! from_bytes {
+ ($ty:tt, $value:expr) => {
+ ($ty::from_le_bytes(match ($value).try_into() {
+ Ok(x) => x,
+ Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))),
+ }))
+ };
+}
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirEvalError::NotSupported(format!($x)))
+ };
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct VTableMap {
+ ty_to_id: FxHashMap<Ty, usize>,
+ id_to_ty: Vec<Ty>,
+}
+
+impl VTableMap {
+ fn id(&mut self, ty: Ty) -> usize {
+ if let Some(x) = self.ty_to_id.get(&ty) {
+ return *x;
+ }
+ let id = self.id_to_ty.len();
+ self.id_to_ty.push(ty.clone());
+ self.ty_to_id.insert(ty, id);
+ id
+ }
+
+ pub(crate) fn ty(&self, id: usize) -> Result<&Ty> {
+ self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
+ }
+
+ fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> {
+ let id = from_bytes!(usize, bytes);
+ self.ty(id)
+ }
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+struct TlsData {
+ keys: Vec<u128>,
+}
+
+impl TlsData {
+ fn create_key(&mut self) -> usize {
+ self.keys.push(0);
+ self.keys.len() - 1
+ }
+
+ fn get_key(&mut self, key: usize) -> Result<u128> {
+ let r = self.keys.get(key).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}"))
+ })?;
+ Ok(*r)
+ }
+
+ fn set_key(&mut self, key: usize, value: u128) -> Result<()> {
+ let r = self.keys.get_mut(key).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}"))
+ })?;
+ *r = value;
+ Ok(())
+ }
+}
+
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>,
heap: Vec<u8>,
+ /// Stores the global location of the statics. We const evaluate every static first time we need it
+ /// and see it's missing, then we add it to this to reuse.
+ static_locations: FxHashMap<StaticId, Address>,
+ /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we
+ /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the
+ /// time of use.
+ vtable_map: VTableMap,
+ thread_local_storage: TlsData,
+ stdout: Vec<u8>,
+ stderr: Vec<u8>,
crate_id: CrateId,
// FIXME: This is a workaround, see the comment on `interpret_mir`
assert_placeholder_ty_is_unused: bool,
@@ -45,19 +138,27 @@ pub struct Evaluator<'a> {
stack_depth_limit: usize,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Address {
Stack(usize),
Heap(usize),
+ Invalid(usize),
}
use Address::*;
+#[derive(Debug, Clone, Copy)]
struct Interval {
addr: Address,
size: usize,
}
+#[derive(Debug, Clone)]
+struct IntervalAndTy {
+ interval: Interval,
+ ty: Ty,
+}
+
impl Interval {
fn new(addr: Address, size: usize) -> Self {
Self { addr, size }
@@ -66,12 +167,49 @@ impl Interval {
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
memory.read_memory(self.addr, self.size)
}
+
+ fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> {
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> {
+ // FIXME: this could be more efficient
+ let bytes = &interval.get(memory)?.to_vec();
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn slice(self, range: Range<usize>) -> Interval {
+ Interval { addr: self.addr.offset(range.start), size: range.len() }
+ }
+}
+
+impl IntervalAndTy {
+ fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+ memory.read_memory(self.interval.addr, self.interval.size)
+ }
+
+ fn new(
+ addr: Address,
+ ty: Ty,
+ evaluator: &Evaluator<'_>,
+ locals: &Locals<'_>,
+ ) -> Result<IntervalAndTy> {
+ let size = evaluator.size_of_sized(&ty, locals, "type of interval")?;
+ Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
+ }
}
enum IntervalOrOwned {
Owned(Vec<u8>),
Borrowed(Interval),
}
+
+impl From<Interval> for IntervalOrOwned {
+ fn from(it: Interval) -> IntervalOrOwned {
+ IntervalOrOwned::Borrowed(it)
+ }
+}
+
impl IntervalOrOwned {
pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result<Vec<u8>> {
Ok(match self {
@@ -79,15 +217,13 @@ impl IntervalOrOwned {
IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(),
})
}
-}
-macro_rules! from_bytes {
- ($ty:tt, $value:expr) => {
- ($ty::from_le_bytes(match ($value).try_into() {
- Ok(x) => x,
- Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
- }))
- };
+ fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+ Ok(match self {
+ IntervalOrOwned::Owned(o) => o,
+ IntervalOrOwned::Borrowed(b) => b.get(memory)?,
+ })
+ }
}
impl Address {
@@ -97,9 +233,11 @@ impl Address {
fn from_usize(x: usize) -> Self {
if x > usize::MAX / 2 {
- Stack(usize::MAX - x)
+ Stack(x - usize::MAX / 2)
+ } else if x > usize::MAX / 4 {
+ Heap(x - usize::MAX / 4)
} else {
- Heap(x)
+ Invalid(x)
}
}
@@ -109,8 +247,9 @@ impl Address {
fn to_usize(&self) -> usize {
let as_num = match self {
- Stack(x) => usize::MAX - *x,
- Heap(x) => *x,
+ Stack(x) => *x + usize::MAX / 2,
+ Heap(x) => *x + usize::MAX / 4,
+ Invalid(x) => *x,
};
as_num
}
@@ -119,6 +258,7 @@ impl Address {
match self {
Stack(x) => Stack(f(*x)),
Heap(x) => Heap(f(*x)),
+ Invalid(x) => Invalid(f(*x)),
}
}
@@ -129,28 +269,123 @@ impl Address {
#[derive(Clone, PartialEq, Eq)]
pub enum MirEvalError {
- ConstEvalError(Box<ConstEvalError>),
+ ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError, Ty),
/// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place.
TypeError(&'static str),
/// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
/// then use this type of error.
- UndefinedBehavior(&'static str),
- Panic,
+ UndefinedBehavior(String),
+ Panic(String),
MirLowerError(FunctionId, MirLowerError),
+ MirLowerErrorForClosure(ClosureId, MirLowerError),
TypeIsUnsized(Ty, &'static str),
NotSupported(String),
InvalidConst(Const),
- InFunction(FunctionId, Box<MirEvalError>),
+ InFunction(Either<FunctionId, ClosureId>, Box<MirEvalError>, MirSpan, DefWithBodyId),
ExecutionLimitExceeded,
StackOverflow,
TargetDataLayoutNotAvailable,
+ InvalidVTableId(usize),
+ CoerceUnsizedError(Ty),
+ LangItemNotFound(LangItem),
+}
+
+impl MirEvalError {
+ pub fn pretty_print(
+ &self,
+ f: &mut String,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ writeln!(f, "Mir eval error:")?;
+ let mut err = self;
+ while let MirEvalError::InFunction(func, e, span, def) = err {
+ err = e;
+ match func {
+ Either::Left(func) => {
+ let function_name = db.function_data(*func);
+ writeln!(
+ f,
+ "In function {} ({:?})",
+ function_name.name.display(db.upcast()),
+ func
+ )?;
+ }
+ Either::Right(clos) => {
+ writeln!(f, "In {:?}", clos)?;
+ }
+ }
+ let source_map = db.body_with_source_map(*def).1;
+ let span: InFile<SyntaxNodePtr> = match span {
+ MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ MirSpan::Unknown => continue,
+ };
+ let file_id = span.file_id.original_file(db.upcast());
+ let text_range = span.value.text_range();
+ writeln!(f, "{}", span_formatter(file_id, text_range))?;
+ }
+ match err {
+ MirEvalError::InFunction(..) => unreachable!(),
+ MirEvalError::LayoutError(err, ty) => {
+ write!(
+ f,
+ "Layout for type `{}` is not available due {err:?}",
+ ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
+ )?;
+ }
+ MirEvalError::MirLowerError(func, err) => {
+ let function_name = db.function_data(*func);
+ writeln!(
+ f,
+ "MIR lowering for function `{}` ({:?}) failed due:",
+ function_name.name.display(db.upcast()),
+ func
+ )?;
+ err.pretty_print(f, db, span_formatter)?;
+ }
+ MirEvalError::ConstEvalError(name, err) => {
+ MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print(
+ f,
+ db,
+ span_formatter,
+ )?;
+ }
+ MirEvalError::TypeError(_)
+ | MirEvalError::UndefinedBehavior(_)
+ | MirEvalError::Panic(_)
+ | MirEvalError::MirLowerErrorForClosure(_, _)
+ | MirEvalError::TypeIsUnsized(_, _)
+ | MirEvalError::NotSupported(_)
+ | MirEvalError::InvalidConst(_)
+ | MirEvalError::ExecutionLimitExceeded
+ | MirEvalError::StackOverflow
+ | MirEvalError::TargetDataLayoutNotAvailable
+ | MirEvalError::CoerceUnsizedError(_)
+ | MirEvalError::LangItemNotFound(_)
+ | MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?,
+ }
+ Ok(())
+ }
}
impl std::fmt::Debug for MirEvalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(),
+ Self::ConstEvalError(arg0, arg1) => {
+ f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish()
+ }
+ Self::LangItemNotFound(arg0) => f.debug_tuple("LangItemNotFound").field(arg0).finish(),
Self::LayoutError(arg0, arg1) => {
f.debug_tuple("LayoutError").field(arg0).field(arg1).finish()
}
@@ -158,7 +393,7 @@ impl std::fmt::Debug for MirEvalError {
Self::UndefinedBehavior(arg0) => {
f.debug_tuple("UndefinedBehavior").field(arg0).finish()
}
- Self::Panic => write!(f, "Panic"),
+ Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"),
Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"),
Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
@@ -166,17 +401,24 @@ impl std::fmt::Debug for MirEvalError {
Self::MirLowerError(arg0, arg1) => {
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
}
+ Self::MirLowerErrorForClosure(arg0, arg1) => {
+ f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
+ }
+ Self::CoerceUnsizedError(arg0) => {
+ f.debug_tuple("CoerceUnsizedError").field(arg0).finish()
+ }
+ Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
Self::InvalidConst(arg0) => {
let data = &arg0.data(Interner);
f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
}
- Self::InFunction(func, e) => {
+ Self::InFunction(func, e, span, _) => {
let mut e = &**e;
- let mut stack = vec![*func];
- while let Self::InFunction(f, next_e) = e {
+ let mut stack = vec![(*func, *span)];
+ while let Self::InFunction(f, next_e, span, _) = e {
e = &next_e;
- stack.push(*f);
+ stack.push((*f, *span));
}
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
}
@@ -184,26 +426,33 @@ impl std::fmt::Debug for MirEvalError {
}
}
-macro_rules! not_supported {
- ($x: expr) => {
- return Err(MirEvalError::NotSupported(format!($x)))
- };
+type Result<T> = std::result::Result<T, MirEvalError>;
+
+#[derive(Debug, Default)]
+struct DropFlags {
+ need_drop: FxHashSet<Place>,
}
-impl From<ConstEvalError> for MirEvalError {
- fn from(value: ConstEvalError) -> Self {
- match value {
- _ => MirEvalError::ConstEvalError(Box::new(value)),
+impl DropFlags {
+ fn add_place(&mut self, p: Place) {
+ if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) {
+ return;
}
+ self.need_drop.retain(|x| !p.is_parent(x));
+ self.need_drop.insert(p);
}
-}
-type Result<T> = std::result::Result<T, MirEvalError>;
+ fn remove_place(&mut self, p: &Place) -> bool {
+ // FIXME: replace parents with parts
+ self.need_drop.remove(p)
+ }
+}
+#[derive(Debug)]
struct Locals<'a> {
- ptr: &'a ArenaMap<LocalId, Address>,
+ ptr: &'a ArenaMap<LocalId, Interval>,
body: &'a MirBody,
- subst: &'a Substitution,
+ drop_flags: DropFlags,
}
pub fn interpret_mir(
@@ -215,38 +464,65 @@ pub fn interpret_mir(
// a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can
// (and probably should) do better here, for example by excluding bindings outside of the target expression.
assert_placeholder_ty_is_unused: bool,
-) -> Result<Const> {
+) -> (Result<Const>, String, String) {
let ty = body.locals[return_slot()].ty.clone();
- let mut evaluator =
- Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
- let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
- let memory_map = evaluator.create_memory_map(
- &bytes,
- &ty,
- &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
- )?;
- return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+ let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
+ let x: Result<Const> = (|| {
+ let bytes = evaluator.interpret_mir(&body, None.into_iter())?;
+ let mut memory_map = evaluator.create_memory_map(
+ &bytes,
+ &ty,
+ &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() },
+ )?;
+ memory_map.vtable = evaluator.vtable_map.clone();
+ return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+ })();
+ (
+ x,
+ String::from_utf8_lossy(&evaluator.stdout).into_owned(),
+ String::from_utf8_lossy(&evaluator.stderr).into_owned(),
+ )
}
impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
- crate_id: CrateId,
+ body: &MirBody,
assert_placeholder_ty_is_unused: bool,
) -> Evaluator<'a> {
+ let crate_id = body.owner.module(db.upcast()).krate();
+ let trait_env = db.trait_environment_for_body(body.owner);
Evaluator {
stack: vec![0],
heap: vec![0],
+ vtable_map: VTableMap::default(),
+ thread_local_storage: TlsData::default(),
+ static_locations: HashMap::default(),
db,
+ trait_env,
crate_id,
+ stdout: vec![],
+ stderr: vec![],
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
- execution_limit: 100_000,
+ execution_limit: 1000_000,
}
}
fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
- Ok(self.place_addr_and_ty(p, locals)?.0)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
+ }
+
+ fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+ let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
+ Ok(Interval {
+ addr: place_addr_and_ty.0,
+ size: self.size_of_sized(
+ &place_addr_and_ty.1,
+ locals,
+ "Type of place that we need its interval",
+ )?,
+ })
}
fn ptr_size(&self) -> usize {
@@ -256,125 +532,170 @@ impl Evaluator<'_> {
}
}
- fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> {
- let mut addr = locals.ptr[p.local];
- let mut ty: Ty =
- self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
- for proj in &p.projection {
+ fn place_addr_and_ty_and_metadata<'a>(
+ &'a self,
+ p: &Place,
+ locals: &'a Locals<'a>,
+ ) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
+ let mut addr = locals.ptr[p.local].addr;
+ let mut ty: Ty = locals.body.locals[p.local].ty.clone();
+ let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
+ for proj in &*p.projection {
+ let prev_ty = ty.clone();
+ ty = proj.projected_ty(
+ ty,
+ self.db,
+ |c, subst, f| {
+ let (def, _) = self.db.lookup_intern_closure(c.into());
+ let infer = self.db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ self.crate_id,
+ );
match proj {
ProjectionElem::Deref => {
- ty = match &ty.data(Interner).kind {
- TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
- _ => {
- return Err(MirEvalError::TypeError(
- "Overloaded deref in MIR is disallowed",
- ))
- }
+ metadata = if self.size_align_of(&ty, locals)?.is_none() {
+ Some(
+ Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }
+ .into(),
+ )
+ } else {
+ None
};
let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
addr = Address::from_usize(x);
}
ProjectionElem::Index(op) => {
- let offset =
- from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
- match &ty.data(Interner).kind {
- TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
- TyKind::Slice(inner) => {
- ty = inner.clone();
- let ty_size = self.size_of_sized(
- &ty,
- locals,
- "slice inner type should be sized",
- )?;
- let value = self.read_memory(addr, self.ptr_size() * 2)?;
- addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
- }
- x => not_supported!("MIR index for ref type {x:?}"),
- },
- TyKind::Array(inner, _) | TyKind::Slice(inner) => {
- ty = inner.clone();
- let ty_size = self.size_of_sized(
- &ty,
- locals,
- "array inner type should be sized",
- )?;
- addr = addr.offset(ty_size * offset);
- }
- x => not_supported!("MIR index for type {x:?}"),
- }
+ let offset = from_bytes!(
+ usize,
+ self.read_memory(locals.ptr[*op].addr, self.ptr_size())?
+ );
+ metadata = None; // Result of index is always sized
+ let ty_size =
+ self.size_of_sized(&ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * offset);
}
- &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
- TyKind::Tuple(_, subst) => {
- let layout = self.layout(&ty)?;
- ty = subst
- .as_slice(Interner)
- .get(f)
- .ok_or(MirEvalError::TypeError("not enough tuple fields"))?
- .assert_ty_ref(Interner)
- .clone();
- let offset = layout.fields.offset(f).bytes_usize();
- addr = addr.offset(offset);
- }
- _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
- },
- ProjectionElem::Field(f) => match &ty.data(Interner).kind {
- TyKind::Adt(adt, subst) => {
- let layout = self.layout_adt(adt.0, subst.clone())?;
- let variant_layout = match &layout.variants {
- Variants::Single { .. } => &layout,
- Variants::Multiple { variants, .. } => {
- &variants[match f.parent {
- hir_def::VariantId::EnumVariantId(x) => {
- RustcEnumVariantIdx(x.local_id)
- }
- _ => {
- return Err(MirEvalError::TypeError(
- "Multivariant layout only happens for enums",
- ))
- }
- }]
- }
+ &ProjectionElem::ConstantIndex { from_end, offset } => {
+ let offset = if from_end {
+ let len = match prev_ty.kind(Interner) {
+ TyKind::Array(_, c) => match try_const_usize(self.db, c) {
+ Some(x) => x as u64,
+ None => {
+ not_supported!("indexing array with unknown const from end")
+ }
+ },
+ TyKind::Slice(_) => match metadata {
+ Some(x) => from_bytes!(u64, x.get(self)?),
+ None => not_supported!("slice place without metadata"),
+ },
+ _ => not_supported!("bad type for const index"),
};
- ty = self.db.field_types(f.parent)[f.local_id]
- .clone()
- .substitute(Interner, subst);
- let offset = variant_layout
- .fields
- .offset(u32::from(f.local_id.into_raw()) as usize)
- .bytes_usize();
- addr = addr.offset(offset);
- }
- _ => return Err(MirEvalError::TypeError("Only adt has fields")),
- },
- ProjectionElem::ConstantIndex { .. } => {
- not_supported!("constant index")
+ (len - offset - 1) as usize
+ } else {
+ offset as usize
+ };
+ metadata = None; // Result of index is always sized
+ let ty_size =
+ self.size_of_sized(&ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * offset);
+ }
+ &ProjectionElem::Subslice { from, to } => {
+ let inner_ty = match &ty.data(Interner).kind {
+ TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
+ _ => TyKind::Error.intern(Interner),
+ };
+ metadata = match metadata {
+ Some(x) => {
+ let prev_len = from_bytes!(u64, x.get(self)?);
+ Some(IntervalOrOwned::Owned(
+ (prev_len - from - to).to_le_bytes().to_vec(),
+ ))
+ }
+ None => None,
+ };
+ let ty_size =
+ self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * (from as usize));
+ }
+ &ProjectionElem::TupleOrClosureField(f) => {
+ let layout = self.layout(&prev_ty)?;
+ let offset = layout.fields.offset(f).bytes_usize();
+ addr = addr.offset(offset);
+ metadata = None; // tuple field is always sized
+ }
+ ProjectionElem::Field(f) => {
+ let layout = self.layout(&prev_ty)?;
+ let variant_layout = match &layout.variants {
+ Variants::Single { .. } => &layout,
+ Variants::Multiple { variants, .. } => {
+ &variants[match f.parent {
+ hir_def::VariantId::EnumVariantId(x) => {
+ RustcEnumVariantIdx(x.local_id)
+ }
+ _ => {
+ return Err(MirEvalError::TypeError(
+ "Multivariant layout only happens for enums",
+ ))
+ }
+ }]
+ }
+ };
+ let offset = variant_layout
+ .fields
+ .offset(u32::from(f.local_id.into_raw()) as usize)
+ .bytes_usize();
+ addr = addr.offset(offset);
+ // FIXME: support structs with unsized fields
+ metadata = None;
}
- ProjectionElem::Subslice { .. } => not_supported!("subslice"),
ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
}
}
- Ok((addr, ty))
+ Ok((addr, ty, metadata))
}
- fn layout(&self, ty: &Ty) -> Result<Layout> {
- layout_of_ty(self.db, ty, self.crate_id)
+ fn layout(&self, ty: &Ty) -> Result<Arc<Layout>> {
+ self.db
+ .layout_of_ty(ty.clone(), self.crate_id)
.map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
}
- fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> {
- self.db.layout_of_adt(adt, subst.clone()).map_err(|e| {
+ fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Arc<Layout>> {
+ self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| {
MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
})
}
fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
- Ok(self.place_addr_and_ty(p, locals)?.1)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
}
- fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result<Ty> {
+ fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result<Ty> {
Ok(match o {
Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
Operand::Constant(c) => c.data(Interner).ty.clone(),
+ &Operand::Static(s) => {
+ let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone();
+ TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner)
+ }
+ })
+ }
+
+ fn operand_ty_and_eval(
+ &mut self,
+ o: &Operand,
+ locals: &mut Locals<'_>,
+ ) -> Result<IntervalAndTy> {
+ Ok(IntervalAndTy {
+ interval: self.eval_operand(o, locals)?,
+ ty: self.operand_ty(o, locals)?,
})
}
@@ -382,7 +703,6 @@ impl Evaluator<'_> {
&mut self,
body: &MirBody,
args: impl Iterator<Item = Vec<u8>>,
- subst: Substitution,
) -> Result<Vec<u8>> {
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
self.stack_depth_limit = x;
@@ -390,7 +710,8 @@ impl Evaluator<'_> {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
- let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst };
+ let mut locals =
+ Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
@@ -401,7 +722,7 @@ impl Evaluator<'_> {
self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
let my_ptr = stack_ptr;
stack_ptr += size;
- Ok((id, Stack(my_ptr)))
+ Ok((id, Interval { addr: Stack(my_ptr), size }))
})
.collect::<Result<ArenaMap<LocalId, _>>>()?;
let stack_size = stack_ptr - self.stack.len();
@@ -409,9 +730,10 @@ impl Evaluator<'_> {
};
locals.ptr = &locals_ptr;
self.stack.extend(iter::repeat(0).take(stack_size));
- let mut remain_args = body.arg_count;
- for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) {
- self.write_memory(*addr, &value)?;
+ let mut remain_args = body.param_locals.len();
+ for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) {
+ locals.drop_flags.add_place(l.into());
+ interval.write_from_bytes(self, &value)?;
if remain_args == 0 {
return Err(MirEvalError::TypeError("more arguments provided"));
}
@@ -431,8 +753,9 @@ impl Evaluator<'_> {
match &statement.kind {
StatementKind::Assign(l, r) => {
let addr = self.place_addr(l, &locals)?;
- let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?;
+ let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
self.write_memory(addr, &result)?;
+ locals.drop_flags.add_place(l.clone());
}
StatementKind::Deinit(_) => not_supported!("de-init statement"),
StatementKind::StorageLive(_)
@@ -443,11 +766,11 @@ impl Evaluator<'_> {
let Some(terminator) = current_block.terminator.as_ref() else {
not_supported!("block without terminator");
};
- match terminator {
- Terminator::Goto { target } => {
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => {
current_block_idx = *target;
}
- Terminator::Call {
+ TerminatorKind::Call {
func,
args,
destination,
@@ -455,155 +778,84 @@ impl Evaluator<'_> {
cleanup: _,
from_hir_call: _,
} => {
+ let destination_interval = self.place_interval(destination, &locals)?;
let fn_ty = self.operand_ty(func, &locals)?;
+ let args = args
+ .iter()
+ .map(|x| self.operand_ty_and_eval(x, &mut locals))
+ .collect::<Result<Vec<_>>>()?;
match &fn_ty.data(Interner).kind {
+ TyKind::Function(_) => {
+ let bytes = self.eval_operand(func, &mut locals)?;
+ self.exec_fn_pointer(
+ bytes,
+ destination_interval,
+ &args,
+ &locals,
+ terminator.span,
+ )?;
+ }
TyKind::FnDef(def, generic_args) => {
- let def: CallableDefId = from_chalk(self.db, *def);
- let generic_args = self.subst_filler(generic_args, &locals);
- match def {
- CallableDefId::FunctionId(def) => {
- let arg_bytes = args
- .iter()
- .map(|x| {
- Ok(self
- .eval_operand(x, &locals)?
- .get(&self)?
- .to_owned())
- })
- .collect::<Result<Vec<_>>>()?
- .into_iter();
- let function_data = self.db.function_data(def);
- let is_intrinsic = match &function_data.abi {
- Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
- None => match def.lookup(self.db.upcast()).container {
- hir_def::ItemContainerId::ExternBlockId(block) => {
- let id = block.lookup(self.db.upcast()).id;
- id.item_tree(self.db.upcast())[id.value]
- .abi
- .as_deref()
- == Some("rust-intrinsic")
- }
- _ => false,
- },
- };
- let result = if is_intrinsic {
- self.exec_intrinsic(
- function_data
- .name
- .as_text()
- .unwrap_or_default()
- .as_str(),
- arg_bytes,
- generic_args,
- &locals,
- )?
- } else if let Some(x) = self.detect_lang_function(def) {
- self.exec_lang_item(x, arg_bytes)?
- } else {
- let trait_env = {
- let Some(d) = body.owner.as_generic_def_id() else {
- not_supported!("trait resolving in non generic def id");
- };
- self.db.trait_environment(d)
- };
- let (imp, generic_args) = lookup_impl_method(
- self.db,
- trait_env,
- def,
- generic_args.clone(),
- );
- let generic_args =
- self.subst_filler(&generic_args, &locals);
- let def = imp.into();
- let mir_body = self
- .db
- .mir_body(def)
- .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
- self.interpret_mir(&mir_body, arg_bytes, generic_args)
- .map_err(|e| {
- MirEvalError::InFunction(imp, Box::new(e))
- })?
- };
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::StructId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::EnumVariantId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- }
- current_block_idx =
- target.expect("broken mir, function without target");
+ self.exec_fn_def(
+ *def,
+ generic_args,
+ destination_interval,
+ &args,
+ &locals,
+ terminator.span,
+ )?;
}
- _ => not_supported!("unknown function type"),
+ x => not_supported!("unknown function type {x:?}"),
}
+ locals.drop_flags.add_place(destination.clone());
+ current_block_idx = target.expect("broken mir, function without target");
}
- Terminator::SwitchInt { discr, targets } => {
+ TerminatorKind::SwitchInt { discr, targets } => {
let val = u128::from_le_bytes(pad16(
- self.eval_operand(discr, &locals)?.get(&self)?,
+ self.eval_operand(discr, &mut locals)?.get(&self)?,
false,
));
current_block_idx = targets.target_for_value(val);
}
- Terminator::Return => {
- let ty = body.locals[return_slot()].ty.clone();
+ TerminatorKind::Return => {
self.stack_depth_limit += 1;
- return Ok(self
- .read_memory(
- locals.ptr[return_slot()],
- self.size_of_sized(&ty, &locals, "return type")?,
- )?
- .to_owned());
+ return Ok(locals.ptr[return_slot()].get(self)?.to_vec());
+ }
+ TerminatorKind::Unreachable => {
+ return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned()));
}
- Terminator::Unreachable => {
- return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
+ TerminatorKind::Drop { place, target, unwind: _ } => {
+ self.drop_place(place, &mut locals, terminator.span)?;
+ current_block_idx = *target;
}
_ => not_supported!("unknown terminator"),
}
}
}
- fn eval_rvalue<'a>(
- &'a mut self,
- r: &'a Rvalue,
- locals: &'a Locals<'a>,
- ) -> Result<IntervalOrOwned> {
+ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<IntervalOrOwned> {
use IntervalOrOwned::*;
Ok(match r {
Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
Rvalue::Ref(_, p) => {
- let addr = self.place_addr(p, locals)?;
- Owned(addr.to_bytes())
+ let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ let mut r = addr.to_bytes();
+ if let Some(metadata) = metadata {
+ r.extend(metadata.get(self)?);
+ }
+ Owned(r)
+ }
+ Rvalue::Len(p) => {
+ let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ match metadata {
+ Some(m) => m,
+ None => {
+ return Err(MirEvalError::TypeError(
+ "type without metadata is used for Rvalue::Len",
+ ));
+ }
+ }
}
- Rvalue::Len(_) => not_supported!("rvalue len"),
Rvalue::UnaryOp(op, val) => {
let mut c = self.eval_operand(val, locals)?.get(&self)?;
let mut ty = self.operand_ty(val, locals)?;
@@ -612,27 +864,40 @@ impl Evaluator<'_> {
let size = self.size_of_sized(&ty, locals, "operand of unary op")?;
c = self.read_memory(Address::from_bytes(c)?, size)?;
}
- let mut c = c.to_vec();
- if ty.as_builtin() == Some(BuiltinType::Bool) {
- c[0] = 1 - c[0];
+ if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
+ match f {
+ chalk_ir::FloatTy::F32 => {
+ let c = -from_bytes!(f32, c);
+ Owned(c.to_le_bytes().into())
+ }
+ chalk_ir::FloatTy::F64 => {
+ let c = -from_bytes!(f64, c);
+ Owned(c.to_le_bytes().into())
+ }
+ }
} else {
- match op {
- UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
- UnOp::Neg => {
- c.iter_mut().for_each(|x| *x = !*x);
- for k in c.iter_mut() {
- let o;
- (*k, o) = k.overflowing_add(1);
- if !o {
- break;
+ let mut c = c.to_vec();
+ if ty.as_builtin() == Some(BuiltinType::Bool) {
+ c[0] = 1 - c[0];
+ } else {
+ match op {
+ UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
+ UnOp::Neg => {
+ c.iter_mut().for_each(|x| *x = !*x);
+ for k in c.iter_mut() {
+ let o;
+ (*k, o) = k.overflowing_add(1);
+ if !o {
+ break;
+ }
}
}
}
}
+ Owned(c)
}
- Owned(c)
}
- Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
+ Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: {
let lc = self.eval_operand(lhs, locals)?;
let rc = self.eval_operand(rhs, locals)?;
let mut lc = lc.get(&self)?;
@@ -640,79 +905,170 @@ impl Evaluator<'_> {
let mut ty = self.operand_ty(lhs, locals)?;
while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
ty = z.clone();
- let size = self.size_of_sized(&ty, locals, "operand of binary op")?;
+ let size = if ty.kind(Interner) == &TyKind::Str {
+ if *op != BinOp::Eq {
+ never!("Only eq is builtin for `str`");
+ }
+ let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]);
+ let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]);
+ if ls != rs {
+ break 'binary_op Owned(vec![0]);
+ }
+ lc = &lc[..self.ptr_size()];
+ rc = &rc[..self.ptr_size()];
+ ls
+ } else {
+ self.size_of_sized(&ty, locals, "operand of binary op")?
+ };
lc = self.read_memory(Address::from_bytes(lc)?, size)?;
rc = self.read_memory(Address::from_bytes(rc)?, size)?;
}
- let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
- let l128 = i128::from_le_bytes(pad16(lc, is_signed));
- let r128 = i128::from_le_bytes(pad16(rc, is_signed));
- match op {
- BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
- let r = match op {
- BinOp::Ge => l128 >= r128,
- BinOp::Gt => l128 > r128,
- BinOp::Le => l128 <= r128,
- BinOp::Lt => l128 < r128,
- BinOp::Eq => l128 == r128,
- BinOp::Ne => l128 != r128,
- _ => unreachable!(),
- };
- let r = r as u8;
- Owned(vec![r])
+ if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
+ match f {
+ chalk_ir::FloatTy::F32 => {
+ let l = from_bytes!(f32, lc);
+ let r = from_bytes!(f32, rc);
+ match op {
+ BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Eq
+ | BinOp::Ne => {
+ let r = op.run_compare(l, r) as u8;
+ Owned(vec![r])
+ }
+ BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
+ let r = match op {
+ BinOp::Add => l + r,
+ BinOp::Sub => l - r,
+ BinOp::Mul => l * r,
+ BinOp::Div => l / r,
+ _ => unreachable!(),
+ };
+ Owned(r.to_le_bytes().into())
+ }
+ x => not_supported!(
+ "invalid binop {x:?} on floating point operators"
+ ),
+ }
+ }
+ chalk_ir::FloatTy::F64 => {
+ let l = from_bytes!(f64, lc);
+ let r = from_bytes!(f64, rc);
+ match op {
+ BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Eq
+ | BinOp::Ne => {
+ let r = op.run_compare(l, r) as u8;
+ Owned(vec![r])
+ }
+ BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
+ let r = match op {
+ BinOp::Add => l + r,
+ BinOp::Sub => l - r,
+ BinOp::Mul => l * r,
+ BinOp::Div => l / r,
+ _ => unreachable!(),
+ };
+ Owned(r.to_le_bytes().into())
+ }
+ x => not_supported!(
+ "invalid binop {x:?} on floating point operators"
+ ),
+ }
+ }
}
- BinOp::BitAnd
- | BinOp::BitOr
- | BinOp::BitXor
- | BinOp::Add
- | BinOp::Mul
- | BinOp::Div
- | BinOp::Rem
- | BinOp::Sub => {
- let r = match op {
- BinOp::Add => l128.overflowing_add(r128).0,
- BinOp::Mul => l128.overflowing_mul(r128).0,
- BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?,
- BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?,
- BinOp::Sub => l128.overflowing_sub(r128).0,
- BinOp::BitAnd => l128 & r128,
- BinOp::BitOr => l128 | r128,
- BinOp::BitXor => l128 ^ r128,
- _ => unreachable!(),
- };
+ } else {
+ let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
+ let l128 = i128::from_le_bytes(pad16(lc, is_signed));
+ let r128 = i128::from_le_bytes(pad16(rc, is_signed));
+ let check_overflow = |r: i128| {
+ // FIXME: this is not very correct, and only catches the basic cases.
let r = r.to_le_bytes();
for &k in &r[lc.len()..] {
if k != 0 && (k != 255 || !is_signed) {
- return Err(MirEvalError::Panic);
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
}
}
- Owned(r[0..lc.len()].into())
- }
- BinOp::Shl | BinOp::Shr => {
- let shift_amout = if r128 < 0 {
- return Err(MirEvalError::Panic);
- } else if r128 > 128 {
- return Err(MirEvalError::Panic);
- } else {
- r128 as u8
- };
- let r = match op {
- BinOp::Shl => l128 << shift_amout,
- BinOp::Shr => l128 >> shift_amout,
- _ => unreachable!(),
- };
- Owned(r.to_le_bytes()[0..lc.len()].into())
+ Ok(Owned(r[0..lc.len()].into()))
+ };
+ match op {
+ BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
+ let r = op.run_compare(l128, r128) as u8;
+ Owned(vec![r])
+ }
+ BinOp::BitAnd
+ | BinOp::BitOr
+ | BinOp::BitXor
+ | BinOp::Add
+ | BinOp::Mul
+ | BinOp::Div
+ | BinOp::Rem
+ | BinOp::Sub => {
+ let r = match op {
+ BinOp::Add => l128.overflowing_add(r128).0,
+ BinOp::Mul => l128.overflowing_mul(r128).0,
+ BinOp::Div => l128.checked_div(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
+ BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
+ BinOp::Sub => l128.overflowing_sub(r128).0,
+ BinOp::BitAnd => l128 & r128,
+ BinOp::BitOr => l128 | r128,
+ BinOp::BitXor => l128 ^ r128,
+ _ => unreachable!(),
+ };
+ check_overflow(r)?
+ }
+ BinOp::Shl | BinOp::Shr => {
+ let r = 'b: {
+ if let Ok(shift_amount) = u32::try_from(r128) {
+ let r = match op {
+ BinOp::Shl => l128.checked_shl(shift_amount),
+ BinOp::Shr => l128.checked_shr(shift_amount),
+ _ => unreachable!(),
+ };
+ if let Some(r) = r {
+ break 'b r;
+ }
+ };
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
+ };
+ check_overflow(r)?
+ }
+ BinOp::Offset => not_supported!("offset binop"),
}
- BinOp::Offset => not_supported!("offset binop"),
}
}
Rvalue::Discriminant(p) => {
let ty = self.place_ty(p, locals)?;
let bytes = self.eval_place(p, locals)?.get(&self)?;
let layout = self.layout(&ty)?;
- match layout.variants {
- Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
- Variants::Multiple { tag, tag_encoding, .. } => {
+ let enum_id = 'b: {
+ match ty.kind(Interner) {
+ TyKind::Adt(e, _) => match e.0 {
+ AdtId::EnumId(e) => break 'b e,
+ _ => (),
+ },
+ _ => (),
+ }
+ return Ok(Owned(0u128.to_le_bytes().to_vec()));
+ };
+ match &layout.variants {
+ Variants::Single { index } => {
+ let r = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: index.0,
+ })?;
+ Owned(r.to_le_bytes().to_vec())
+ }
+ Variants::Multiple { tag, tag_encoding, variants, .. } => {
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
not_supported!("missing target data layout");
};
@@ -725,120 +1081,142 @@ impl Evaluator<'_> {
}
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
let tag = &bytes[offset..offset + size];
- let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
- .wrapping_sub(niche_start as i128);
- let enum_id = match ty.kind(Interner) {
- TyKind::Adt(e, _) => match e.0 {
- AdtId::EnumId(e) => e,
- _ => not_supported!("Non enum with multi variant layout"),
- },
- _ => not_supported!("Non adt with multi variant layout"),
- };
- let enum_data = self.db.enum_data(enum_id);
- let result = 'b: {
- for (local_id, _) in enum_data.variants.iter() {
- if candidate_discriminant
- == self.db.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id,
- })?
- {
- break 'b candidate_discriminant;
- }
- }
- self.db.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id: untagged_variant.0,
- })?
- };
+ let candidate_tag = i128::from_le_bytes(pad16(tag, false))
+ .wrapping_sub(*niche_start as i128)
+ as usize;
+ let variant = variants
+ .iter_enumerated()
+ .map(|(x, _)| x)
+ .filter(|x| x != untagged_variant)
+ .nth(candidate_tag)
+ .unwrap_or(*untagged_variant)
+ .0;
+ let result = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: variant,
+ })?;
Owned(result.to_le_bytes().to_vec())
}
}
}
}
}
+ Rvalue::Repeat(x, len) => {
+ let len = match try_const_usize(self.db, &len) {
+ Some(x) => x as usize,
+ None => not_supported!("non evaluatable array len in repeat Rvalue"),
+ };
+ let val = self.eval_operand(x, locals)?.get(self)?;
+ let size = len * val.len();
+ Owned(val.iter().copied().cycle().take(size).collect())
+ }
Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"),
+ Rvalue::ShallowInitBoxWithAlloc(ty) => {
+ let Some((size, align)) = self.size_align_of(ty, locals)? else {
+ not_supported!("unsized box initialization");
+ };
+ let addr = self.heap_allocate(size, align);
+ Owned(addr.to_bytes())
+ }
Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
- Rvalue::Aggregate(kind, values) => match kind {
- AggregateKind::Array(_) => {
- let mut r = vec![];
- for x in values {
- let value = self.eval_operand(x, locals)?.get(&self)?;
- r.extend(value);
+ Rvalue::Aggregate(kind, values) => {
+ let values = values
+ .iter()
+ .map(|x| self.eval_operand(x, locals))
+ .collect::<Result<Vec<_>>>()?;
+ match kind {
+ AggregateKind::Array(_) => {
+ let mut r = vec![];
+ for x in values {
+ let value = x.get(&self)?;
+ r.extend(value);
+ }
+ Owned(r)
+ }
+ AggregateKind::Tuple(ty) => {
+ let layout = self.layout(&ty)?;
+ Owned(self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ values.iter().map(|&x| x.into()),
+ )?)
+ }
+ AggregateKind::Union(x, f) => {
+ let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+ let offset = layout
+ .fields
+ .offset(u32::from(f.local_id.into_raw()) as usize)
+ .bytes_usize();
+ let op = values[0].get(&self)?;
+ let mut result = vec![0; layout.size.bytes_usize()];
+ result[offset..offset + op.len()].copy_from_slice(op);
+ Owned(result)
+ }
+ AggregateKind::Adt(x, subst) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(*x, subst.clone(), locals)?;
+ Owned(self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ values.iter().map(|&x| x.into()),
+ )?)
+ }
+ AggregateKind::Closure(ty) => {
+ let layout = self.layout(&ty)?;
+ Owned(self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ values.iter().map(|&x| x.into()),
+ )?)
}
- Owned(r)
- }
- AggregateKind::Tuple(ty) => {
- let layout = self.layout(&ty)?;
- Owned(self.make_by_layout(
- layout.size.bytes_usize(),
- &layout,
- None,
- values,
- locals,
- )?)
- }
- AggregateKind::Union(x, f) => {
- let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
- let offset = layout
- .fields
- .offset(u32::from(f.local_id.into_raw()) as usize)
- .bytes_usize();
- let op = self.eval_operand(&values[0], locals)?.get(&self)?;
- let mut result = vec![0; layout.size.bytes_usize()];
- result[offset..offset + op.len()].copy_from_slice(op);
- Owned(result)
- }
- AggregateKind::Adt(x, subst) => {
- let (size, variant_layout, tag) =
- self.layout_of_variant(*x, subst.clone(), locals)?;
- Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
}
- },
+ }
Rvalue::Cast(kind, operand, target_ty) => match kind {
- CastKind::PointerExposeAddress => not_supported!("exposing pointer address"),
- CastKind::PointerFromExposedAddress => {
- not_supported!("creating pointer from exposed address")
- }
CastKind::Pointer(cast) => match cast {
- PointerCast::Unsize => {
+ PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => {
let current_ty = self.operand_ty(operand, locals)?;
- match &target_ty.data(Interner).kind {
- TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
- match &ty.data(Interner).kind {
- TyKind::Slice(_) => match &current_ty.data(Interner).kind {
- TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
- match &ty.data(Interner).kind {
- TyKind::Array(_, size) => {
- let addr = self
- .eval_operand(operand, locals)?
- .get(&self)?;
- let len = const_as_usize(size);
- let mut r = Vec::with_capacity(16);
- r.extend(addr.iter().copied());
- r.extend(len.to_le_bytes().into_iter());
- Owned(r)
- }
- _ => {
- not_supported!("slice unsizing from non arrays")
- }
- }
- }
- _ => not_supported!("slice unsizing from non pointers"),
- },
- TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"),
- _ => not_supported!("unknown unsized cast"),
- }
- }
- _ => not_supported!("unsized cast on unknown pointer type"),
+ if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) =
+ &current_ty.data(Interner).kind
+ {
+ let id = self.vtable_map.id(current_ty);
+ let ptr_size = self.ptr_size();
+ Owned(id.to_le_bytes()[0..ptr_size].to_vec())
+ } else {
+ not_supported!(
+ "creating a fn pointer from a non FnDef or Closure type"
+ );
}
}
- x => not_supported!("pointer cast {x:?}"),
+ PointerCast::Unsize => {
+ let current_ty = self.operand_ty(operand, locals)?;
+ let addr = self.eval_operand(operand, locals)?;
+ self.coerce_unsized(addr, &current_ty, target_ty)?
+ }
+ PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => {
+ // This is no-op
+ Borrowed(self.eval_operand(operand, locals)?)
+ }
+ PointerCast::ArrayToPointer => {
+ // We should remove the metadata part if the current type is slice
+ Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size()))
+ }
},
CastKind::DynStar => not_supported!("dyn star cast"),
- CastKind::IntToInt => {
- // FIXME: handle signed cast
- let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
+ CastKind::IntToInt
+ | CastKind::PointerExposeAddress
+ | CastKind::PointerFromExposedAddress => {
+ let current_ty = self.operand_ty(operand, locals)?;
+ let is_signed = match current_ty.kind(Interner) {
+ TyKind::Scalar(s) => match s {
+ chalk_ir::Scalar::Int(_) => true,
+ _ => false,
+ },
+ _ => false,
+ };
+ let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, is_signed);
let dest_size =
self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
Owned(current[0..dest_size].to_vec())
@@ -846,31 +1224,106 @@ impl Evaluator<'_> {
CastKind::FloatToInt => not_supported!("float to int cast"),
CastKind::FloatToFloat => not_supported!("float to float cast"),
CastKind::IntToFloat => not_supported!("float to int cast"),
- CastKind::PtrToPtr => not_supported!("ptr to ptr cast"),
CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
},
})
}
+ fn coerce_unsized_look_through_fields<T>(
+ &self,
+ ty: &Ty,
+ goal: impl Fn(&TyKind) -> Option<T>,
+ ) -> Result<T> {
+ let kind = ty.kind(Interner);
+ if let Some(x) = goal(kind) {
+ return Ok(x);
+ }
+ if let TyKind::Adt(id, subst) = kind {
+ if let AdtId::StructId(struct_id) = id.0 {
+ let field_types = self.db.field_types(struct_id.into());
+ let mut field_types = field_types.iter();
+ if let Some(ty) =
+ field_types.next().map(|x| x.1.clone().substitute(Interner, subst))
+ {
+ return self.coerce_unsized_look_through_fields(&ty, goal);
+ }
+ }
+ }
+ Err(MirEvalError::CoerceUnsizedError(ty.clone()))
+ }
+
+ fn coerce_unsized(
+ &mut self,
+ addr: Interval,
+ current_ty: &Ty,
+ target_ty: &Ty,
+ ) -> Result<IntervalOrOwned> {
+ use IntervalOrOwned::*;
+ fn for_ptr(x: &TyKind) -> Option<Ty> {
+ match x {
+ TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
+ _ => None,
+ }
+ }
+ Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
+ ty => match &ty.data(Interner).kind {
+ TyKind::Slice(_) => {
+ match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
+ ty => match &ty.data(Interner).kind {
+ TyKind::Array(_, size) => {
+ let len = match try_const_usize(self.db, size) {
+ None => not_supported!(
+ "unevaluatble len of array in coerce unsized"
+ ),
+ Some(x) => x as usize,
+ };
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(len.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ t => {
+ not_supported!("slice unsizing from non array type {t:?}")
+ }
+ },
+ }
+ }
+ TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
+ TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+ let vtable = self.vtable_map.id(ty.clone());
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(vtable.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ _ => not_supported!("dyn unsizing from non pointers"),
+ },
+ _ => not_supported!("unknown unsized cast"),
+ },
+ })
+ }
+
fn layout_of_variant(
&mut self,
x: VariantId,
subst: Substitution,
locals: &Locals<'_>,
- ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+ ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = x.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let VariantId::EnumVariantId(x) = x {
if AdtId::from(f.parent) == adt {
// Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
// infinite sized type errors) we use a dummy layout
- let i = self.db.const_eval_discriminant(x)?;
+ let i = self.const_eval_discriminant(x)?;
return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i))));
}
}
}
let layout = self.layout_adt(adt, subst)?;
- Ok(match layout.variants {
+ Ok(match &layout.variants {
Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
Variants::Multiple { variants, tag, tag_encoding, .. } => {
let cx = self
@@ -882,18 +1335,27 @@ impl Evaluator<'_> {
_ => not_supported!("multi variant layout for non-enums"),
};
let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id);
- let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?;
+ let mut discriminant = self.const_eval_discriminant(enum_variant_id)?;
let variant_layout = variants[rustc_enum_variant_idx].clone();
let have_tag = match tag_encoding {
TagEncoding::Direct => true,
TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
- discriminant = discriminant.wrapping_add(niche_start as i128);
- untagged_variant != rustc_enum_variant_idx
+ if *untagged_variant == rustc_enum_variant_idx {
+ false
+ } else {
+ discriminant = (variants
+ .iter_enumerated()
+ .filter(|(x, _)| x != untagged_variant)
+ .position(|(x, _)| x == rustc_enum_variant_idx)
+ .unwrap() as i128)
+ .wrapping_add(*niche_start as i128);
+ true
+ }
}
};
(
layout.size.bytes_usize(),
- variant_layout,
+ Arc::new(variant_layout),
if have_tag {
Some((
layout.fields.offset(0).bytes_usize(),
@@ -910,71 +1372,84 @@ impl Evaluator<'_> {
fn make_by_layout(
&mut self,
- size: usize, // Not neccessarily equal to variant_layout.size
+ size: usize, // Not necessarily equal to variant_layout.size
variant_layout: &Layout,
tag: Option<(usize, usize, i128)>,
- values: &Vec<Operand>,
- locals: &Locals<'_>,
+ values: impl Iterator<Item = IntervalOrOwned>,
) -> Result<Vec<u8>> {
let mut result = vec![0; size];
if let Some((offset, size, value)) = tag {
result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
}
- for (i, op) in values.iter().enumerate() {
+ for (i, op) in values.enumerate() {
let offset = variant_layout.fields.offset(i).bytes_usize();
- let op = self.eval_operand(op, locals)?.get(&self)?;
+ let op = op.get(&self)?;
result[offset..offset + op.len()].copy_from_slice(op);
}
Ok(result)
}
- fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result<Interval> {
+ fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result<Interval> {
Ok(match x {
- Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?,
+ Operand::Copy(p) | Operand::Move(p) => {
+ locals.drop_flags.remove_place(p);
+ self.eval_place(p, locals)?
+ }
+ Operand::Static(st) => {
+ let addr = self.eval_static(*st, locals)?;
+ Interval::new(addr, self.ptr_size())
+ }
Operand::Constant(konst) => {
let data = &konst.data(Interner);
match &data.value {
- chalk_ir::ConstValue::BoundVar(b) => {
- let c = locals
- .subst
- .as_slice(Interner)
- .get(b.index)
- .ok_or(MirEvalError::TypeError("missing generic arg"))?
- .assert_const_ref(Interner);
- self.eval_operand(&Operand::Constant(c.clone()), locals)?
- }
+ chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"),
chalk_ir::ConstValue::InferenceVar(_) => {
not_supported!("inference var constant")
}
chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"),
- chalk_ir::ConstValue::Concrete(c) => match &c.interned {
- ConstScalar::Bytes(v, memory_map) => {
- let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
- let patch_map = memory_map.transform_addresses(|b| {
- let addr = self.heap_allocate(b.len());
- self.write_memory(addr, b)?;
- Ok(addr.to_usize())
- })?;
- let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len());
- if size != v.len() {
- // Handle self enum
- if size == 16 && v.len() < 16 {
- v = Cow::Owned(pad16(&v, false).to_vec());
- } else if size < 16 && v.len() == 16 {
- v = Cow::Owned(v[0..size].to_vec());
- } else {
- return Err(MirEvalError::InvalidConst(konst.clone()));
- }
- }
- let addr = self.heap_allocate(size);
- self.write_memory(addr, &v)?;
- self.patch_addresses(&patch_map, addr, &data.ty, locals)?;
- Interval::new(addr, size)
- }
- ConstScalar::Unknown => not_supported!("evaluating unknown const"),
- },
+ chalk_ir::ConstValue::Concrete(c) => {
+ self.allocate_const_in_heap(c, &data.ty, locals, konst)?
+ }
+ }
+ }
+ })
+ }
+
+ fn allocate_const_in_heap(
+ &mut self,
+ c: &chalk_ir::ConcreteConst<Interner>,
+ ty: &Ty,
+ locals: &Locals<'_>,
+ konst: &chalk_ir::Const<Interner>,
+ ) -> Result<Interval> {
+ Ok(match &c.interned {
+ ConstScalar::Bytes(v, memory_map) => {
+ let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
+ let patch_map = memory_map.transform_addresses(|b| {
+ let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong
+ self.write_memory(addr, b)?;
+ Ok(addr.to_usize())
+ })?;
+ let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
+ if size != v.len() {
+ // Handle self enum
+ if size == 16 && v.len() < 16 {
+ v = Cow::Owned(pad16(&v, false).to_vec());
+ } else if size < 16 && v.len() == 16 {
+ v = Cow::Owned(v[0..size].to_vec());
+ } else {
+ return Err(MirEvalError::InvalidConst(konst.clone()));
+ }
}
+ let addr = self.heap_allocate(size, align);
+ self.write_memory(addr, &v)?;
+ self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
+ Interval::new(addr, size)
+ }
+ ConstScalar::UnevaluatedConst(..) => {
+ not_supported!("unevaluated const present in monomorphized mir");
}
+ ConstScalar::Unknown => not_supported!("evaluating unknown const"),
})
}
@@ -987,197 +1462,213 @@ impl Evaluator<'_> {
}
fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> {
+ if size == 0 {
+ return Ok(&[]);
+ }
let (mem, pos) = match addr {
Stack(x) => (&self.stack, x),
Heap(x) => (&self.heap, x),
+ Invalid(x) => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "read invalid memory address {x} with size {size}"
+ )));
+ }
};
- mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
+ mem.get(pos..pos + size)
+ .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string()))
}
fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+ if r.is_empty() {
+ return Ok(());
+ }
let (mem, pos) = match addr {
Stack(x) => (&mut self.stack, x),
Heap(x) => (&mut self.heap, x),
+ Invalid(x) => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "write invalid memory address {x} with content {r:?}"
+ )));
+ }
};
mem.get_mut(pos..pos + r.len())
- .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
+ .ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
+ })?
.copy_from_slice(r);
Ok(())
}
- fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<usize>> {
+ fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<(usize, usize)>> {
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let Some((adt, _)) = ty.as_adt() {
if AdtId::from(f.parent) == adt {
// Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
// infinite sized type errors) we use a dummy size
- return Ok(Some(16));
+ return Ok(Some((16, 16)));
}
}
}
- let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?;
let layout = self.layout(ty);
if self.assert_placeholder_ty_is_unused {
if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
- return Ok(Some(0));
+ return Ok(Some((0, 1)));
}
}
let layout = layout?;
- Ok(layout.is_sized().then(|| layout.size.bytes_usize()))
+ Ok(layout
+ .is_sized()
+ .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)))
}
/// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
/// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> {
- match self.size_of(ty, locals)? {
- Some(x) => Ok(x),
+ match self.size_align_of(ty, locals)? {
+ Some(x) => Ok(x.0),
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
}
}
- /// Uses `ty_filler` to fill an entire subst
- fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
- Substitution::from_iter(
- Interner,
- subst.iter(Interner).map(|x| match x.data(Interner) {
- chalk_ir::GenericArgData::Ty(ty) => {
- let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
- return x.clone();
- };
- chalk_ir::GenericArgData::Ty(ty).intern(Interner)
- }
- _ => x.clone(),
- }),
- )
- }
-
- /// This function substitutes placeholders of the body with the provided subst, effectively plays
- /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
- /// position impl traits) with their underlying type.
- fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
- struct Filler<'a> {
- db: &'a dyn HirDatabase,
- subst: &'a Substitution,
- skip_params: usize,
- }
- impl FallibleTypeFolder<Interner> for Filler<'_> {
- type Error = MirEvalError;
-
- fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
- self
- }
-
- fn interner(&self) -> Interner {
- Interner
- }
-
- fn try_fold_ty(
- &mut self,
- ty: Ty,
- outer_binder: DebruijnIndex,
- ) -> std::result::Result<Ty, Self::Error> {
- match ty.kind(Interner) {
- TyKind::OpaqueType(id, subst) => {
- let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
- match impl_trait_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- let infer = self.db.infer(func.into());
- let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
- filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
- }
- crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
- not_supported!("async block impl trait");
- }
- }
- }
- _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
- }
- }
-
- fn try_fold_free_placeholder_ty(
- &mut self,
- idx: chalk_ir::PlaceholderIndex,
- _outer_binder: DebruijnIndex,
- ) -> std::result::Result<Ty, Self::Error> {
- let x = from_placeholder_idx(self.db, idx);
- Ok(self
- .subst
- .as_slice(Interner)
- .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params)
- .and_then(|x| x.ty(Interner))
- .ok_or(MirEvalError::TypeError("Generic arg not provided"))?
- .clone())
- }
- }
- let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
- Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?))
- }
-
- fn heap_allocate(&mut self, s: usize) -> Address {
+ fn heap_allocate(&mut self, size: usize, _align: usize) -> Address {
let pos = self.heap.len();
- self.heap.extend(iter::repeat(0).take(s));
+ self.heap.extend(iter::repeat(0).take(size));
Address::Heap(pos)
}
- pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
- self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
- }
-
- fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
- let candidate = lang_attr(self.db.upcast(), def)?;
- // filter normal lang functions out
- if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
+ fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
+ use LangItem::*;
+ let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
return None;
+ };
+ let l = lang_attr(self.db.upcast(), parent)?;
+ match l {
+ FnOnce => Some(FnTrait::FnOnce),
+ FnMut => Some(FnTrait::FnMut),
+ Fn => Some(FnTrait::Fn),
+ _ => None,
}
- Some(candidate)
}
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
- // FIXME: support indirect references
- let mut mm = MemoryMap::default();
- match ty.kind(Interner) {
- TyKind::Ref(_, _, t) => {
- let size = self.size_of(t, locals)?;
- match size {
- Some(size) => {
- let addr_usize = from_bytes!(usize, bytes);
- mm.insert(
- addr_usize,
- self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
- )
- }
- None => {
- let element_size = match t.kind(Interner) {
- TyKind::Str => 1,
- TyKind::Slice(t) => {
- self.size_of_sized(t, locals, "slice inner type")?
+ fn rec(
+ this: &Evaluator<'_>,
+ bytes: &[u8],
+ ty: &Ty,
+ locals: &Locals<'_>,
+ mm: &mut MemoryMap,
+ ) -> Result<()> {
+ match ty.kind(Interner) {
+ TyKind::Ref(_, _, t) => {
+ let size = this.size_align_of(t, locals)?;
+ match size {
+ Some((size, _)) => {
+ let addr_usize = from_bytes!(usize, bytes);
+ mm.insert(
+ addr_usize,
+ this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
+ )
+ }
+ None => {
+ let mut check_inner = None;
+ let (addr, meta) = bytes.split_at(bytes.len() / 2);
+ let element_size = match t.kind(Interner) {
+ TyKind::Str => 1,
+ TyKind::Slice(t) => {
+ check_inner = Some(t);
+ this.size_of_sized(t, locals, "slice inner type")?
+ }
+ TyKind::Dyn(_) => {
+ let t = this.vtable_map.ty_of_bytes(meta)?;
+ check_inner = Some(t);
+ this.size_of_sized(t, locals, "dyn concrete type")?
+ }
+ _ => return Ok(()),
+ };
+ let count = match t.kind(Interner) {
+ TyKind::Dyn(_) => 1,
+ _ => from_bytes!(usize, meta),
+ };
+ let size = element_size * count;
+ let addr = Address::from_bytes(addr)?;
+ let b = this.read_memory(addr, size)?;
+ mm.insert(addr.to_usize(), b.to_vec());
+ if let Some(ty) = check_inner {
+ for i in 0..count {
+ let offset = element_size * i;
+ rec(this, &b[offset..offset + element_size], &ty, locals, mm)?;
+ }
}
- _ => return Ok(mm), // FIXME: support other kind of unsized types
- };
- let (addr, meta) = bytes.split_at(bytes.len() / 2);
- let size = element_size * from_bytes!(usize, meta);
- let addr = Address::from_bytes(addr)?;
- mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec());
+ }
}
}
+ chalk_ir::TyKind::Tuple(_, subst) => {
+ let layout = this.layout(ty)?;
+ for (id, ty) in subst.iter(Interner).enumerate() {
+ let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
+ let offset = layout.fields.offset(id).bytes_usize();
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+ AdtId::StructId(s) => {
+ let data = this.db.struct_data(s);
+ let layout = this.layout(ty)?;
+ let field_types = this.db.field_types(s.into());
+ for (f, _) in data.variant_data.fields().iter() {
+ let offset = layout
+ .fields
+ .offset(u32::from(f.into_raw()) as usize)
+ .bytes_usize();
+ let ty = &field_types[f].clone().substitute(Interner, subst);
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ AdtId::EnumId(e) => {
+ let layout = this.layout(ty)?;
+ if let Some((v, l)) =
+ detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e)
+ {
+ let data = &this.db.enum_data(e).variants[v].variant_data;
+ let field_types = this
+ .db
+ .field_types(EnumVariantId { parent: e, local_id: v }.into());
+ for (f, _) in data.fields().iter() {
+ let offset =
+ l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
+ let ty = &field_types[f].clone().substitute(Interner, subst);
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ }
+ AdtId::UnionId(_) => (),
+ },
+ _ => (),
}
- _ => (),
+ Ok(())
}
+ let mut mm = MemoryMap::default();
+ rec(self, bytes, ty, locals, &mut mm)?;
Ok(mm)
}
fn patch_addresses(
&mut self,
patch_map: &HashMap<usize, usize>,
+ old_vtable: &VTableMap,
addr: Address,
ty: &Ty,
locals: &Locals<'_>,
) -> Result<()> {
// FIXME: support indirect references
+ let layout = self.layout(ty)?;
let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
match ty.kind(Interner) {
TyKind::Ref(_, _, t) => {
- let size = self.size_of(t, locals)?;
+ let size = self.size_align_of(t, locals)?;
match size {
Some(_) => {
let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
@@ -1193,51 +1684,441 @@ impl Evaluator<'_> {
}
}
}
- _ => (),
+ TyKind::Function(_) => {
+ let ty = old_vtable.ty_of_bytes(self.read_memory(addr, my_size)?)?.clone();
+ let new_id = self.vtable_map.id(ty);
+ self.write_memory(addr, &new_id.to_le_bytes())?;
+ }
+ TyKind::Adt(id, subst) => match id.0 {
+ AdtId::StructId(s) => {
+ for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() {
+ let offset = layout.fields.offset(i).bytes_usize();
+ let ty = ty.clone().substitute(Interner, subst);
+ self.patch_addresses(
+ patch_map,
+ old_vtable,
+ addr.offset(offset),
+ &ty,
+ locals,
+ )?;
+ }
+ }
+ AdtId::UnionId(_) => (),
+ AdtId::EnumId(_) => (),
+ },
+ TyKind::AssociatedType(_, _)
+ | TyKind::Scalar(_)
+ | TyKind::Tuple(_, _)
+ | TyKind::Array(_, _)
+ | TyKind::Slice(_)
+ | TyKind::Raw(_, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::FnDef(_, _)
+ | TyKind::Str
+ | TyKind::Never
+ | TyKind::Closure(_, _)
+ | TyKind::Generator(_, _)
+ | TyKind::GeneratorWitness(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Dyn(_)
+ | TyKind::Alias(_)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => (),
}
Ok(())
}
- fn exec_intrinsic(
- &self,
- as_str: &str,
- _arg_bytes: impl Iterator<Item = Vec<u8>>,
- generic_args: Substitution,
+ fn exec_fn_pointer(
+ &mut self,
+ bytes: Interval,
+ destination: Interval,
+ args: &[IntervalAndTy],
locals: &Locals<'_>,
- ) -> Result<Vec<u8>> {
- match as_str {
- "size_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
- return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
- };
- let size = self.size_of(ty, locals)?;
- match size {
- Some(x) => Ok(x.to_le_bytes().to_vec()),
- None => return Err(MirEvalError::TypeError("size_of arg is unsized")),
+ span: MirSpan,
+ ) -> Result<()> {
+ let id = from_bytes!(usize, bytes.get(self)?);
+ let next_ty = self.vtable_map.ty(id)?.clone();
+ match &next_ty.data(Interner).kind {
+ TyKind::FnDef(def, generic_args) => {
+ self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?;
+ }
+ TyKind::Closure(id, subst) => {
+ self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?;
+ }
+ _ => return Err(MirEvalError::TypeError("function pointer to non function")),
+ }
+ Ok(())
+ }
+
+ fn exec_closure(
+ &mut self,
+ closure: ClosureId,
+ closure_data: Interval,
+ generic_args: &Substitution,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let mir_body = self
+ .db
+ .monomorphized_mir_body_for_closure(
+ closure,
+ generic_args.clone(),
+ self.trait_env.clone(),
+ )
+ .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
+ let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some()
+ {
+ closure_data.addr.to_bytes()
+ } else {
+ closure_data.get(self)?.to_owned()
+ };
+ let arg_bytes = iter::once(Ok(closure_data))
+ .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
+ .collect::<Result<Vec<_>>>()?;
+ let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| {
+ MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner)
+ })?;
+ destination.write_from_bytes(self, &bytes)
+ }
+
+ fn exec_fn_def(
+ &mut self,
+ def: FnDefId,
+ generic_args: &Substitution,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let def: CallableDefId = from_chalk(self.db, def);
+ let generic_args = generic_args.clone();
+ match def {
+ CallableDefId::FunctionId(def) => {
+ if let Some(_) = self.detect_fn_trait(def) {
+ self.exec_fn_trait(&args, destination, locals, span)?;
+ return Ok(());
}
+ self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?;
+ }
+ CallableDefId::StructId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args, &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval.into()),
+ )?;
+ destination.write_from_bytes(self, &result)?;
+ }
+ CallableDefId::EnumVariantId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args, &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval.into()),
+ )?;
+ destination.write_from_bytes(self, &result)?;
}
- _ => not_supported!("unknown intrinsic {as_str}"),
}
+ Ok(())
}
- pub(crate) fn exec_lang_item(
- &self,
- x: LangItem,
- mut args: std::vec::IntoIter<Vec<u8>>,
- ) -> Result<Vec<u8>> {
- use LangItem::*;
- match x {
- PanicFmt | BeginPanic => Err(MirEvalError::Panic),
- SliceLen => {
- let arg = args
- .next()
- .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
- let ptr_size = arg.len() / 2;
- Ok(arg[ptr_size..].into())
+ fn exec_fn_with_args(
+ &mut self,
+ def: FunctionId,
+ args: &[IntervalAndTy],
+ generic_args: Substitution,
+ locals: &Locals<'_>,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<()> {
+ if self.detect_and_exec_special_function(
+ def,
+ args,
+ &generic_args,
+ locals,
+ destination,
+ span,
+ )? {
+ return Ok(());
+ }
+ let arg_bytes =
+ args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ if let Some(self_ty_idx) =
+ is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone())
+ {
+ // In the layout of current possible receiver, which at the moment of writing this code is one of
+ // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers,
+ // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on
+ // the type.
+ let ty =
+ self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?;
+ let mut args_for_target = args.to_vec();
+ args_for_target[0] = IntervalAndTy {
+ interval: args_for_target[0].interval.slice(0..self.ptr_size()),
+ ty: ty.clone(),
+ };
+ let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+ let generics_for_target =
+ Substitution::from_iter(
+ Interner,
+ generic_args.iter(Interner).enumerate().map(|(i, x)| {
+ if i == self_ty_idx {
+ &ty
+ } else {
+ x
+ }
+ }),
+ );
+ return self.exec_fn_with_args(
+ def,
+ &args_for_target,
+ generics_for_target,
+ locals,
+ destination,
+ span,
+ );
+ }
+ let (imp, generic_args) =
+ lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args);
+ self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination)
+ }
+
+ fn exec_looked_up_function(
+ &mut self,
+ generic_args: Substitution,
+ locals: &Locals<'_>,
+ imp: FunctionId,
+ arg_bytes: Vec<Vec<u8>>,
+ span: MirSpan,
+ destination: Interval,
+ ) -> Result<()> {
+ let def = imp.into();
+ let mir_body = self
+ .db
+ .monomorphized_mir_body(def, generic_args, self.trait_env.clone())
+ .map_err(|e| {
+ MirEvalError::InFunction(
+ Either::Left(imp),
+ Box::new(MirEvalError::MirLowerError(imp, e)),
+ span,
+ locals.body.owner,
+ )
+ })?;
+ let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| {
+ MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
+ })?;
+ destination.write_from_bytes(self, &result)?;
+ Ok(())
+ }
+
+ fn exec_fn_trait(
+ &mut self,
+ args: &[IntervalAndTy],
+ destination: Interval,
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
+ let mut func_ty = func.ty.clone();
+ let mut func_data = func.interval;
+ while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) {
+ func_ty = z.clone();
+ if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) {
+ let id =
+ from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]);
+ func_data = func_data.slice(0..self.ptr_size());
+ func_ty = self.vtable_map.ty(id)?.clone();
+ }
+ let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?;
+ func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size };
+ }
+ match &func_ty.data(Interner).kind {
+ TyKind::FnDef(def, subst) => {
+ self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?;
+ }
+ TyKind::Function(_) => {
+ self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?;
+ }
+ TyKind::Closure(closure, subst) => {
+ self.exec_closure(
+ *closure,
+ func_data,
+ &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
+ destination,
+ &args[1..],
+ locals,
+ span,
+ )?;
+ }
+ x => not_supported!("Call FnTrait methods with type {x:?}"),
+ }
+ Ok(())
+ }
+
+ fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result<Address> {
+ if let Some(o) = self.static_locations.get(&st) {
+ return Ok(*o);
+ };
+ let static_data = self.db.static_data(st);
+ let result = if !static_data.is_extern {
+ let konst = self.db.const_eval_static(st).map_err(|e| {
+ MirEvalError::ConstEvalError(
+ static_data.name.as_str().unwrap_or("_").to_owned(),
+ Box::new(e),
+ )
+ })?;
+ let data = &konst.data(Interner);
+ if let chalk_ir::ConstValue::Concrete(c) = &data.value {
+ self.allocate_const_in_heap(&c, &data.ty, locals, &konst)?
+ } else {
+ not_supported!("unevaluatable static");
+ }
+ } else {
+ let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr];
+ let Some((size, align)) = self.size_align_of(&ty, locals)? else {
+ not_supported!("unsized extern static");
+ };
+ let addr = self.heap_allocate(size, align);
+ Interval::new(addr, size)
+ };
+ let addr = self.heap_allocate(self.ptr_size(), self.ptr_size());
+ self.write_memory(addr, &result.addr.to_bytes())?;
+ self.static_locations.insert(st, addr);
+ Ok(addr)
+ }
+
+ fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
+ let r = self.db.const_eval_discriminant(variant);
+ match r {
+ Ok(r) => Ok(r),
+ Err(e) => {
+ let data = self.db.enum_data(variant.parent);
+ let name = format!(
+ "{}::{}",
+ data.name.display(self.db.upcast()),
+ data.variants[variant.local_id].name.display(self.db.upcast())
+ );
+ Err(MirEvalError::ConstEvalError(name, Box::new(e)))
}
- x => not_supported!("Executing lang item {x:?}"),
}
}
+
+ fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> {
+ let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
+ if !locals.drop_flags.remove_place(place) {
+ return Ok(());
+ }
+ let metadata = match metadata {
+ Some(x) => x.get(self)?.to_vec(),
+ None => vec![],
+ };
+ self.run_drop_glue_deep(ty, locals, addr, &metadata, span)
+ }
+
+ fn run_drop_glue_deep(
+ &mut self,
+ ty: Ty,
+ locals: &Locals<'_>,
+ addr: Address,
+ _metadata: &[u8],
+ span: MirSpan,
+ ) -> Result<()> {
+ let Some(drop_fn) = (|| {
+ let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?;
+ self.db.trait_data(drop_trait).method_by_name(&name![drop])
+ })() else {
+ // in some tests we don't have drop trait in minicore, and
+ // we can ignore drop in them.
+ return Ok(());
+ };
+ let (impl_drop_candidate, subst) = lookup_impl_method(
+ self.db,
+ self.trait_env.clone(),
+ drop_fn,
+ Substitution::from1(Interner, ty.clone()),
+ );
+ if impl_drop_candidate != drop_fn {
+ self.exec_looked_up_function(
+ subst,
+ locals,
+ impl_drop_candidate,
+ vec![addr.to_bytes()],
+ span,
+ Interval { addr: Address::Invalid(0), size: 0 },
+ )?;
+ }
+ match ty.kind(Interner) {
+ TyKind::Adt(id, subst) => {
+ match id.0 {
+ AdtId::StructId(s) => {
+ let data = self.db.struct_data(s);
+ if data.flags.contains(StructFlags::IS_MANUALLY_DROP) {
+ return Ok(());
+ }
+ let layout = self.layout_adt(id.0, subst.clone())?;
+ match data.variant_data.as_ref() {
+ VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ let field_types = self.db.field_types(s.into());
+ for (field, _) in fields.iter() {
+ let offset = layout
+ .fields
+ .offset(u32::from(field.into_raw()) as usize)
+ .bytes_usize();
+ let addr = addr.offset(offset);
+ let ty = field_types[field].clone().substitute(Interner, subst);
+ self.run_drop_glue_deep(ty, locals, addr, &[], span)?;
+ }
+ }
+ VariantData::Unit => (),
+ }
+ }
+ AdtId::UnionId(_) => (), // union fields don't need drop
+ AdtId::EnumId(_) => (),
+ }
+ }
+ TyKind::AssociatedType(_, _)
+ | TyKind::Scalar(_)
+ | TyKind::Tuple(_, _)
+ | TyKind::Array(_, _)
+ | TyKind::Slice(_)
+ | TyKind::Raw(_, _)
+ | TyKind::Ref(_, _, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::FnDef(_, _)
+ | TyKind::Str
+ | TyKind::Never
+ | TyKind::Closure(_, _)
+ | TyKind::Generator(_, _)
+ | TyKind::GeneratorWitness(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Dyn(_)
+ | TyKind::Alias(_)
+ | TyKind::Function(_)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => (),
+ };
+ Ok(())
+ }
+
+ fn write_to_stdout(&mut self, interval: Interval) -> Result<()> {
+ self.stdout.extend(interval.get(self)?.to_vec());
+ Ok(())
+ }
+
+ fn write_to_stderr(&mut self, interval: Interval) -> Result<()> {
+ self.stderr.extend(interval.get(self)?.to_vec());
+ Ok(())
+ }
}
pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
new file mode 100644
index 000000000..3b9ef03c3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -0,0 +1,792 @@
+//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
+//! is not available.
+
+use std::cmp;
+
+use super::*;
+
+macro_rules! from_bytes {
+ ($ty:tt, $value:expr) => {
+ ($ty::from_le_bytes(match ($value).try_into() {
+ Ok(x) => x,
+ Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+ }))
+ };
+}
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirEvalError::NotSupported(format!($x)))
+ };
+}
+
+impl Evaluator<'_> {
+ pub(super) fn detect_and_exec_special_function(
+ &mut self,
+ def: FunctionId,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ locals: &Locals<'_>,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<bool> {
+ let function_data = self.db.function_data(def);
+ let is_intrinsic = match &function_data.abi {
+ Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+ None => match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref()
+ == Some("rust-intrinsic")
+ }
+ _ => false,
+ },
+ };
+ if is_intrinsic {
+ self.exec_intrinsic(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ span,
+ )?;
+ return Ok(true);
+ }
+ let is_extern_c = match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref() == Some("C")
+ }
+ _ => false,
+ };
+ if is_extern_c {
+ self.exec_extern_c(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ span,
+ )?;
+ return Ok(true);
+ }
+ let alloc_fn = function_data
+ .attrs
+ .iter()
+ .filter_map(|x| x.path().as_ident())
+ .filter_map(|x| x.as_str())
+ .find(|x| {
+ [
+ "rustc_allocator",
+ "rustc_deallocator",
+ "rustc_reallocator",
+ "rustc_allocator_zeroed",
+ ]
+ .contains(x)
+ });
+ if let Some(alloc_fn) = alloc_fn {
+ self.exec_alloc_fn(alloc_fn, args, destination)?;
+ return Ok(true);
+ }
+ if let Some(x) = self.detect_lang_function(def) {
+ let arg_bytes =
+ args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?;
+ destination.write_from_bytes(self, &result)?;
+ return Ok(true);
+ }
+ Ok(false)
+ }
+
+ fn exec_alloc_fn(
+ &mut self,
+ alloc_fn: &str,
+ args: &[IntervalAndTy],
+ destination: Interval,
+ ) -> Result<()> {
+ match alloc_fn {
+ "rustc_allocator_zeroed" | "rustc_allocator" => {
+ let [size, align] = args else {
+ return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
+ };
+ let size = from_bytes!(usize, size.get(self)?);
+ let align = from_bytes!(usize, align.get(self)?);
+ let result = self.heap_allocate(size, align);
+ destination.write_from_bytes(self, &result.to_bytes())?;
+ }
+ "rustc_deallocator" => { /* no-op for now */ }
+ "rustc_reallocator" => {
+ let [ptr, old_size, align, new_size] = args else {
+ return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
+ };
+ let ptr = Address::from_bytes(ptr.get(self)?)?;
+ let old_size = from_bytes!(usize, old_size.get(self)?);
+ let new_size = from_bytes!(usize, new_size.get(self)?);
+ let align = from_bytes!(usize, align.get(self)?);
+ let result = self.heap_allocate(new_size, align);
+ Interval { addr: result, size: old_size }
+ .write_from_interval(self, Interval { addr: ptr, size: old_size })?;
+ destination.write_from_bytes(self, &result.to_bytes())?;
+ }
+ _ => not_supported!("unknown alloc function"),
+ }
+ Ok(())
+ }
+
+ fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
+ use LangItem::*;
+ let candidate = lang_attr(self.db.upcast(), def)?;
+ // We want to execute these functions with special logic
+ if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
+ return Some(candidate);
+ }
+ None
+ }
+
+ fn exec_lang_item(
+ &mut self,
+ x: LangItem,
+ generic_args: &Substitution,
+ args: &[Vec<u8>],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<Vec<u8>> {
+ use LangItem::*;
+ let mut args = args.iter();
+ match x {
+ BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
+ PanicFmt => {
+ let message = (|| {
+ let arguments_struct =
+ self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?;
+ let arguments_layout = self
+ .layout_adt(arguments_struct.into(), Substitution::empty(Interner))
+ .ok()?;
+ let arguments_field_pieces =
+ self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?;
+ let pieces_offset = arguments_layout
+ .fields
+ .offset(u32::from(arguments_field_pieces.into_raw()) as usize)
+ .bytes_usize();
+ let ptr_size = self.ptr_size();
+ let arg = args.next()?;
+ let pieces_array_addr =
+ Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?;
+ let pieces_array_len = usize::from_le_bytes(
+ (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size])
+ .try_into()
+ .ok()?,
+ );
+ let mut message = "".to_string();
+ for i in 0..pieces_array_len {
+ let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size);
+ let piece_addr =
+ Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?)
+ .ok()?;
+ let piece_len = usize::from_le_bytes(
+ self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size)
+ .ok()?
+ .try_into()
+ .ok()?,
+ );
+ let piece_data = self.read_memory(piece_addr, piece_len).ok()?;
+ message += &std::string::String::from_utf8_lossy(piece_data);
+ }
+ Some(message)
+ })()
+ .unwrap_or_else(|| "<format-args-evaluation-failed>".to_string());
+ Err(MirEvalError::Panic(message))
+ }
+ SliceLen => {
+ let arg = args
+ .next()
+ .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
+ let ptr_size = arg.len() / 2;
+ Ok(arg[ptr_size..].into())
+ }
+ DropInPlace => {
+ let ty =
+ generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or(
+ MirEvalError::TypeError(
+ "generic argument of drop_in_place is not provided",
+ ),
+ )?;
+ let arg = args
+ .next()
+ .ok_or(MirEvalError::TypeError("argument of drop_in_place is not provided"))?;
+ self.run_drop_glue_deep(
+ ty.clone(),
+ locals,
+ Address::from_bytes(&arg[0..self.ptr_size()])?,
+ &arg[self.ptr_size()..],
+ span,
+ )?;
+ Ok(vec![])
+ }
+ x => not_supported!("Executing lang item {x:?}"),
+ }
+ }
+
+ fn exec_extern_c(
+ &mut self,
+ as_str: &str,
+ args: &[IntervalAndTy],
+ _generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ _span: MirSpan,
+ ) -> Result<()> {
+ match as_str {
+ "memcmp" => {
+ let [ptr1, ptr2, size] = args else {
+ return Err(MirEvalError::TypeError("memcmp args are not provided"));
+ };
+ let addr1 = Address::from_bytes(ptr1.get(self)?)?;
+ let addr2 = Address::from_bytes(ptr2.get(self)?)?;
+ let size = from_bytes!(usize, size.get(self)?);
+ let slice1 = self.read_memory(addr1, size)?;
+ let slice2 = self.read_memory(addr2, size)?;
+ let r: i128 = match slice1.cmp(slice2) {
+ cmp::Ordering::Less => -1,
+ cmp::Ordering::Equal => 0,
+ cmp::Ordering::Greater => 1,
+ };
+ destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size])
+ }
+ "write" => {
+ let [fd, ptr, len] = args else {
+ return Err(MirEvalError::TypeError("libc::write args are not provided"));
+ };
+ let fd = u128::from_le_bytes(pad16(fd.get(self)?, false));
+ let interval = Interval {
+ addr: Address::from_bytes(ptr.get(self)?)?,
+ size: from_bytes!(usize, len.get(self)?),
+ };
+ match fd {
+ 1 => {
+ self.write_to_stdout(interval)?;
+ }
+ 2 => {
+ self.write_to_stderr(interval)?;
+ }
+ _ => not_supported!("write to arbitrary file descriptor"),
+ }
+ destination.write_from_interval(self, len.interval)?;
+ Ok(())
+ }
+ "pthread_key_create" => {
+ let key = self.thread_local_storage.create_key();
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_key_create arg0 is not provided"));
+ };
+ let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
+ let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() {
+ ty
+ } else {
+ return Err(MirEvalError::TypeError(
+ "pthread_key_create arg0 is not a pointer",
+ ));
+ };
+ let arg0_interval = Interval::new(
+ arg0_addr,
+ self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?,
+ );
+ arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_getspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let value = self.thread_local_storage.get_key(key)?;
+ destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_setspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let Some(arg1) = args.get(1) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided"));
+ };
+ let value = from_bytes!(u128, pad16(arg1.get(self)?, false));
+ self.thread_local_storage.set_key(key, value)?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_key_delete" => {
+ // we ignore this currently
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ _ => not_supported!("unknown external function {as_str}"),
+ }
+ }
+
+ fn exec_intrinsic(
+ &mut self,
+ name: &str,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ if let Some(name) = name.strip_prefix("atomic_") {
+ return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span);
+ }
+ if let Some(name) = name.strip_suffix("f64") {
+ let result = match name {
+ "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
+ | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64"));
+ };
+ let arg = from_bytes!(f64, arg.get(self)?);
+ match name {
+ "sqrt" => arg.sqrt(),
+ "sin" => arg.sin(),
+ "cos" => arg.cos(),
+ "exp" => arg.exp(),
+ "exp2" => arg.exp2(),
+ "log" => arg.ln(),
+ "log10" => arg.log10(),
+ "log2" => arg.log2(),
+ "fabs" => arg.abs(),
+ "floor" => arg.floor(),
+ "ceil" => arg.ceil(),
+ "trunc" => arg.trunc(),
+ // FIXME: these rounds should be different, but only `.round()` is stable now.
+ "rint" => arg.round(),
+ "nearbyint" => arg.round(),
+ "round" => arg.round(),
+ "roundeven" => arg.round(),
+ _ => unreachable!(),
+ }
+ }
+ "pow" | "minnum" | "maxnum" | "copysign" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(f64, arg2.get(self)?);
+ match name {
+ "pow" => arg1.powf(arg2),
+ "minnum" => arg1.min(arg2),
+ "maxnum" => arg1.max(arg2),
+ "copysign" => arg1.copysign(arg2),
+ _ => unreachable!(),
+ }
+ }
+ "powi" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(i32, arg2.get(self)?);
+ arg1.powi(arg2)
+ }
+ "fma" => {
+ let [arg1, arg2, arg3] = args else {
+ return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(f64, arg2.get(self)?);
+ let arg3 = from_bytes!(f64, arg3.get(self)?);
+ arg1.mul_add(arg2, arg3)
+ }
+ _ => not_supported!("unknown f64 intrinsic {name}"),
+ };
+ return destination.write_from_bytes(self, &result.to_le_bytes());
+ }
+ if let Some(name) = name.strip_suffix("f32") {
+ let result = match name {
+ "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
+ | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32"));
+ };
+ let arg = from_bytes!(f32, arg.get(self)?);
+ match name {
+ "sqrt" => arg.sqrt(),
+ "sin" => arg.sin(),
+ "cos" => arg.cos(),
+ "exp" => arg.exp(),
+ "exp2" => arg.exp2(),
+ "log" => arg.ln(),
+ "log10" => arg.log10(),
+ "log2" => arg.log2(),
+ "fabs" => arg.abs(),
+ "floor" => arg.floor(),
+ "ceil" => arg.ceil(),
+ "trunc" => arg.trunc(),
+ // FIXME: these rounds should be different, but only `.round()` is stable now.
+ "rint" => arg.round(),
+ "nearbyint" => arg.round(),
+ "round" => arg.round(),
+ "roundeven" => arg.round(),
+ _ => unreachable!(),
+ }
+ }
+ "pow" | "minnum" | "maxnum" | "copysign" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(f32, arg2.get(self)?);
+ match name {
+ "pow" => arg1.powf(arg2),
+ "minnum" => arg1.min(arg2),
+ "maxnum" => arg1.max(arg2),
+ "copysign" => arg1.copysign(arg2),
+ _ => unreachable!(),
+ }
+ }
+ "powi" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(i32, arg2.get(self)?);
+ arg1.powi(arg2)
+ }
+ "fma" => {
+ let [arg1, arg2, arg3] = args else {
+ return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(f32, arg2.get(self)?);
+ let arg3 = from_bytes!(f32, arg3.get(self)?);
+ arg1.mul_add(arg2, arg3)
+ }
+ _ => not_supported!("unknown f32 intrinsic {name}"),
+ };
+ return destination.write_from_bytes(self, &result.to_le_bytes());
+ }
+ match name {
+ "size_of" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+ };
+ let size = self.size_of_sized(ty, locals, "size_of arg")?;
+ destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
+ }
+ "min_align_of" | "pref_align_of" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
+ };
+ let align = self.layout(ty)?.align.abi.bytes();
+ destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
+ }
+ "needs_drop" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+ };
+ let result = !ty.clone().is_copy(self.db, locals.body.owner);
+ destination.write_from_bytes(self, &[u8::from(result)])
+ }
+ "ptr_guaranteed_cmp" => {
+ // FIXME: this is wrong for const eval, it should return 2 in some
+ // cases.
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
+ };
+ let ans = lhs.get(self)? == rhs.get(self)?;
+ destination.write_from_bytes(self, &[u8::from(ans)])
+ }
+ "saturating_add" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("saturating_add args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.saturating_add(rhs);
+ let bits = destination.size * 8;
+ // FIXME: signed
+ let is_signed = false;
+ let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 };
+ // FIXME: signed
+ let mn: u128 = 0;
+ let ans = cmp::min(mx, cmp::max(mn, ans));
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_add" | "unchecked_add" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_add(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_sub(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_mul" | "unchecked_mul" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_mul args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_mul(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_rem" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_rem args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_rem(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_div" | "exact_div" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_div args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_div(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let result_ty = TyKind::Tuple(
+ 2,
+ Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]),
+ )
+ .intern(Interner);
+ let op_size =
+ self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?;
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let (ans, u128overflow) = match name {
+ "add_with_overflow" => lhs.overflowing_add(rhs),
+ "sub_with_overflow" => lhs.overflowing_sub(rhs),
+ "mul_with_overflow" => lhs.overflowing_mul(rhs),
+ _ => unreachable!(),
+ };
+ let is_overflow = u128overflow
+ || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255);
+ let is_overflow = vec![u8::from(is_overflow)];
+ let layout = self.layout(&result_ty)?;
+ let result = self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ [ans.to_le_bytes()[0..op_size].to_vec(), is_overflow]
+ .into_iter()
+ .map(IntervalOrOwned::Owned),
+ )?;
+ destination.write_from_bytes(self, &result)
+ }
+ "copy" | "copy_nonoverlapping" => {
+ let [src, dst, offset] = args else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided"));
+ };
+ let src = Address::from_bytes(src.get(self)?)?;
+ let dst = Address::from_bytes(dst.get(self)?)?;
+ let offset = from_bytes!(usize, offset.get(self)?);
+ let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
+ let size = offset * size;
+ let src = Interval { addr: src, size };
+ let dst = Interval { addr: dst, size };
+ dst.write_from_interval(self, src)
+ }
+ "offset" | "arith_offset" => {
+ let [ptr, offset] = args else {
+ return Err(MirEvalError::TypeError("offset args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
+ let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
+ let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128;
+ let ans = ptr + offset * size;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => {
+ // FIXME: We should actually implement these checks
+ Ok(())
+ }
+ "forget" => {
+ // We don't call any drop glue yet, so there is nothing here
+ Ok(())
+ }
+ "transmute" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("trasmute arg is not provided"));
+ };
+ destination.write_from_interval(self, arg.interval)
+ }
+ "likely" | "unlikely" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ destination.write_from_interval(self, arg.interval)
+ }
+ "ctpop" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
+ "cttz" | "cttz_nonzero" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
+ "const_eval_select" => {
+ let [tuple, const_fn, _] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let mut args = vec![const_fn.clone()];
+ let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else {
+ return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple"));
+ };
+ let layout = self.layout(&tuple.ty)?;
+ for (i, field) in fields.iter(Interner).enumerate() {
+ let field = field.assert_ty_ref(Interner).clone();
+ let offset = layout.fields.offset(i).bytes_usize();
+ let addr = tuple.interval.addr.offset(offset);
+ args.push(IntervalAndTy::new(addr, field, self, locals)?);
+ }
+ self.exec_fn_trait(&args, destination, locals, span)
+ }
+ _ => not_supported!("unknown intrinsic {name}"),
+ }
+ }
+
+ fn exec_atomic_intrinsic(
+ &mut self,
+ name: &str,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ _span: MirSpan,
+ ) -> Result<()> {
+ // We are a single threaded runtime with no UB checking and no optimization, so
+ // we can implement these as normal functions.
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided"));
+ };
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided"));
+ };
+ let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
+ let arg0_interval =
+ Interval::new(arg0_addr, self.size_of_sized(ty, locals, "atomic intrinsic type arg")?);
+ if name.starts_with("load_") {
+ return destination.write_from_interval(self, arg0_interval);
+ }
+ let Some(arg1) = args.get(1) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided"));
+ };
+ if name.starts_with("store_") {
+ return arg0_interval.write_from_interval(self, arg1.interval);
+ }
+ if name.starts_with("xchg_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ return arg0_interval.write_from_interval(self, arg1.interval);
+ }
+ if name.starts_with("xadd_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs.wrapping_add(rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("xsub_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs.wrapping_sub(rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("and_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs & rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("or_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs | rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("xor_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs ^ rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("nand_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = !(lhs & rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ let Some(arg2) = args.get(2) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided"));
+ };
+ if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") {
+ let dest = if arg1.get(self)? == arg0_interval.get(self)? {
+ arg0_interval.write_from_interval(self, arg2.interval)?;
+ (arg1.interval, true)
+ } else {
+ (arg0_interval, false)
+ };
+ let result_ty = TyKind::Tuple(
+ 2,
+ Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]),
+ )
+ .intern(Interner);
+ let layout = self.layout(&result_ty)?;
+ let result = self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ [IntervalOrOwned::Borrowed(dest.0), IntervalOrOwned::Owned(vec![u8::from(dest.1)])]
+ .into_iter(),
+ )?;
+ return destination.write_from_bytes(self, &result);
+ }
+ not_supported!("unknown atomic intrinsic {name}");
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
new file mode 100644
index 000000000..ca4268b8f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -0,0 +1,676 @@
+use base_db::{fixture::WithFixture, FileId};
+use hir_def::db::DefDatabase;
+use syntax::{TextRange, TextSize};
+
+use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
+
+use super::{interpret_mir, MirEvalError};
+
+fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalError> {
+ let module_id = db.module_for_file(file_id);
+ let def_map = module_id.def_map(db);
+ let scope = &def_map[module_id.local_id].scope;
+ let func_id = scope
+ .declarations()
+ .find_map(|x| match x {
+ hir_def::ModuleDefId::FunctionId(x) => {
+ if db.function_data(x).name.display(db).to_string() == "main" {
+ Some(x)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ .expect("no main function found");
+ let body = db
+ .monomorphized_mir_body(
+ func_id.into(),
+ Substitution::empty(Interner),
+ db.trait_environment(func_id.into()),
+ )
+ .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
+ let (result, stdout, stderr) = interpret_mir(db, &body, false);
+ result?;
+ Ok((stdout, stderr))
+}
+
+fn check_pass(ra_fixture: &str) {
+ check_pass_and_stdio(ra_fixture, "", "");
+}
+
+fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) {
+ let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+ let file_id = *file_ids.last().unwrap();
+ let x = eval_main(&db, file_id);
+ match x {
+ Err(e) => {
+ let mut err = String::new();
+ let line_index = |size: TextSize| {
+ let mut size = u32::from(size) as usize;
+ let mut lines = ra_fixture.lines().enumerate();
+ while let Some((i, l)) = lines.next() {
+ if let Some(x) = size.checked_sub(l.len()) {
+ size = x;
+ } else {
+ return (i, size);
+ }
+ }
+ (usize::MAX, size)
+ };
+ let span_formatter = |file, range: TextRange| {
+ format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end()))
+ };
+ e.pretty_print(&mut err, &db, span_formatter).unwrap();
+ panic!("Error in interpreting: {err}");
+ }
+ Ok((stdout, stderr)) => {
+ assert_eq!(stdout, expected_stdout);
+ assert_eq!(stderr, expected_stderr);
+ }
+ }
+}
+
+#[test]
+fn function_with_extern_c_abi() {
+ check_pass(
+ r#"
+extern "C" fn foo(a: i32, b: i32) -> i32 {
+ a + b
+}
+
+fn main() {
+ let x = foo(2, 3);
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_basic() {
+ check_pass(
+ r#"
+//- minicore: drop, add
+
+struct X<'a>(&'a mut i32);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ *self.0 += 1;
+ }
+}
+
+struct NestedX<'a> { f1: X<'a>, f2: X<'a> }
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn my_drop2(x: X<'_>) {
+ return;
+}
+
+fn my_drop(x: X<'_>) {
+ drop(x);
+}
+
+fn main() {
+ let mut s = 10;
+ let mut x = X(&mut s);
+ my_drop(x);
+ x = X(&mut s);
+ my_drop2(x);
+ X(&mut s); // dropped immediately
+ let x = X(&mut s);
+ NestedX { f1: x, f2: X(&mut s) };
+ if s != 15 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_if_let() {
+ check_pass(
+ r#"
+//- minicore: drop, add, option, cell, builtin_impls
+
+use core::cell::Cell;
+
+struct X<'a>(&'a Cell<i32>);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ self.0.set(self.0.get() + 1)
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+#[test]
+fn main() {
+ let s = Cell::new(0);
+ let x = Some(X(&s));
+ if let Some(y) = x {
+ if s.get() != 0 {
+ should_not_reach();
+ }
+ if s.get() != 0 {
+ should_not_reach();
+ }
+ } else {
+ should_not_reach();
+ }
+ if s.get() != 1 {
+ should_not_reach();
+ }
+ let x = Some(X(&s));
+ if let None = x {
+ should_not_reach();
+ } else {
+ if s.get() != 1 {
+ should_not_reach();
+ }
+ }
+ if s.get() != 1 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_in_place() {
+ check_pass(
+ r#"
+//- minicore: drop, add, coerce_unsized
+use core::ptr::drop_in_place;
+
+struct X<'a>(&'a mut i32);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ *self.0 += 1;
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let mut s = 2;
+ let x = X(&mut s);
+ drop_in_place(&mut x);
+ drop(x);
+ if s != 4 {
+ should_not_reach();
+ }
+ let p: &mut [X] = &mut [X(&mut 2)];
+ drop_in_place(p);
+}
+ "#,
+ );
+}
+
+#[test]
+fn manually_drop() {
+ check_pass(
+ r#"
+//- minicore: manually_drop
+use core::mem::ManuallyDrop;
+
+struct X;
+impl Drop for X {
+ fn drop(&mut self) {
+ should_not_reach();
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let x = ManuallyDrop::new(X);
+}
+ "#,
+ );
+}
+
+#[test]
+fn generic_impl_for_trait_with_generic_method() {
+ check_pass(
+ r#"
+//- minicore: drop
+struct S<T>(T);
+
+trait Tr {
+ fn f<F>(&self, x: F);
+}
+
+impl<T> Tr for S<T> {
+ fn f<F>(&self, x: F) {
+ }
+}
+
+fn main() {
+ let s = S(1u8);
+ s.f(5i64);
+}
+ "#,
+ );
+}
+
+#[test]
+fn index_of_slice_should_preserve_len() {
+ check_pass(
+ r#"
+//- minicore: index, slice, coerce_unsized
+
+struct X;
+
+impl core::ops::Index<X> for [i32] {
+ type Output = i32;
+
+ fn index(&self, _: X) -> &i32 {
+ if self.len() != 3 {
+ should_not_reach();
+ }
+ &self[0]
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let x: &[i32] = &[1, 2, 3];
+ &x[X];
+}
+ "#,
+ );
+}
+
+#[test]
+fn memcmp() {
+ check_pass(
+ r#"
+//- minicore: slice, coerce_unsized, index
+
+fn should_not_reach() -> bool {
+ _ // FIXME: replace this function with panic when that works
+}
+
+extern "C" {
+ fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32;
+}
+
+fn my_cmp(x: &[u8], y: &[u8]) -> i32 {
+ memcmp(x as *const u8, y as *const u8, x.len())
+}
+
+fn main() {
+ if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 {
+ should_not_reach();
+ }
+ if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 {
+ should_not_reach();
+ }
+ if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn unix_write_stdout() {
+ check_pass_and_stdio(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+type pthread_key_t = u32;
+type c_void = u8;
+type c_int = i32;
+
+extern "C" {
+ pub fn write(fd: i32, buf: *const u8, count: usize) -> usize;
+}
+
+fn main() {
+ let stdout = b"stdout";
+ let stderr = b"stderr";
+ write(1, &stdout[0], 6);
+ write(2, &stderr[0], 6);
+}
+ "#,
+ "stdout",
+ "stderr",
+ );
+}
+
+#[test]
+fn closure_layout_in_rpit() {
+ check_pass(
+ r#"
+//- minicore: fn
+
+fn f<F: Fn()>(x: F) {
+ fn g(x: impl Fn()) -> impl FnOnce() {
+ move || {
+ x();
+ }
+ }
+ g(x)();
+}
+
+fn main() {
+ f(|| {});
+}
+ "#,
+ );
+}
+
+#[test]
+fn from_fn() {
+ check_pass(
+ r#"
+//- minicore: fn, iterator
+struct FromFn<F>(F);
+
+impl<T, F: FnMut() -> Option<T>> Iterator for FromFn<F> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ (self.0)()
+ }
+}
+
+fn main() {
+ let mut tokenize = {
+ FromFn(move || Some(2))
+ };
+ let s = tokenize.next();
+}
+ "#,
+ );
+}
+
+#[test]
+fn for_loop() {
+ check_pass(
+ r#"
+//- minicore: iterator, add
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+struct X;
+struct XIter(i32);
+
+impl IntoIterator for X {
+ type Item = i32;
+
+ type IntoIter = XIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ XIter(0)
+ }
+}
+
+impl Iterator for XIter {
+ type Item = i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.0 == 5 {
+ None
+ } else {
+ self.0 += 1;
+ Some(self.0)
+ }
+ }
+}
+
+fn main() {
+ let mut s = 0;
+ for x in X {
+ s += x;
+ }
+ if s != 15 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn field_with_associated_type() {
+ check_pass(
+ r#"
+//- /b/mod.rs crate:b
+pub trait Tr {
+ fn f(self);
+}
+
+pub trait Tr2 {
+ type Ty: Tr;
+}
+
+pub struct S<T: Tr2> {
+ pub t: T::Ty,
+}
+
+impl<T: Tr2> S<T> {
+ pub fn g(&self) {
+ let k = (self.t, self.t);
+ self.t.f();
+ }
+}
+
+//- /a/mod.rs crate:a deps:b
+use b::{Tr, Tr2, S};
+
+struct A(i32);
+struct B(u8);
+
+impl Tr for A {
+ fn f(&self) {
+ }
+}
+
+impl Tr2 for B {
+ type Ty = A;
+}
+
+#[test]
+fn main() {
+ let s: S<B> = S { t: A(2) };
+ s.g();
+}
+ "#,
+ );
+}
+
+#[test]
+fn specialization_array_clone() {
+ check_pass(
+ r#"
+//- minicore: copy, derive, slice, index, coerce_unsized
+impl<T: Clone, const N: usize> Clone for [T; N] {
+ #[inline]
+ fn clone(&self) -> Self {
+ SpecArrayClone::clone(self)
+ }
+}
+
+trait SpecArrayClone: Clone {
+ fn clone<const N: usize>(array: &[Self; N]) -> [Self; N];
+}
+
+impl<T: Clone> SpecArrayClone for T {
+ #[inline]
+ default fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ // FIXME: panic here when we actually implement specialization.
+ from_slice(array)
+ }
+}
+
+fn from_slice<T, const N: usize>(s: &[T]) -> [T; N] {
+ [s[0]; N]
+}
+
+impl<T: Copy> SpecArrayClone for T {
+ #[inline]
+ fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ *array
+ }
+}
+
+#[derive(Clone, Copy)]
+struct X(i32);
+
+fn main() {
+ let ar = [X(1), X(2)];
+ ar.clone();
+}
+ "#,
+ );
+}
+
+#[test]
+fn short_circuit_operator() {
+ check_pass(
+ r#"
+fn should_not_reach() -> bool {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ if false && should_not_reach() {
+ should_not_reach();
+ }
+ true || should_not_reach();
+
+}
+ "#,
+ );
+}
+
+#[test]
+fn closure_state() {
+ check_pass(
+ r#"
+//- minicore: fn, add, copy
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let mut x = 2;
+ let mut c = move || {
+ x += 1;
+ x
+ };
+ c();
+ c();
+ c();
+ if x != 2 {
+ should_not_reach();
+ }
+ if c() != 6 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn closure_capture_array_const_generic() {
+ check_pass(
+ r#"
+//- minicore: fn, add, copy
+struct X(i32);
+
+fn f<const N: usize>(mut x: [X; N]) { // -> impl FnOnce() {
+ let c = || {
+ x;
+ };
+ c();
+}
+
+fn main() {
+ let s = f([X(1)]);
+ //s();
+}
+ "#,
+ );
+}
+
+#[test]
+fn posix_tls() {
+ check_pass(
+ r#"
+//- minicore: option
+
+type pthread_key_t = u32;
+type c_void = u8;
+type c_int = i32;
+
+extern "C" {
+ pub fn pthread_key_create(
+ key: *mut pthread_key_t,
+ dtor: Option<unsafe extern "C" fn(*mut c_void)>,
+ ) -> c_int;
+ pub fn pthread_key_delete(key: pthread_key_t) -> c_int;
+ pub fn pthread_getspecific(key: pthread_key_t) -> *mut c_void;
+ pub fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int;
+}
+
+fn main() {
+ let mut key = 2;
+ pthread_key_create(&mut key, None);
+}
+ "#,
+ );
+}
+
+#[test]
+fn regression_14966() {
+ check_pass(
+ r#"
+//- minicore: fn, copy, coerce_unsized
+trait A<T> {
+ fn a(&self) {}
+}
+impl A<()> for () {}
+
+struct B;
+impl B {
+ pub fn b<T>(s: &dyn A<T>) -> Self {
+ B
+ }
+}
+struct C;
+impl C {
+ fn c<T>(a: &dyn A<T>) -> Self {
+ let mut c = C;
+ let b = B::b(a);
+ c.d(|| a.a());
+ c
+ }
+ fn d(&mut self, f: impl FnOnce()) {}
+}
+
+fn main() {
+ C::c(&());
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index c4dd7c0ac..2cb29b4ab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -1,60 +1,88 @@
//! This module generates a polymorphic MIR from a hir body
-use std::{iter, mem, sync::Arc};
+use std::{fmt::Write, iter, mem};
+use base_db::FileId;
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
body::Body,
- expr::{
- Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
- RecordLitField,
+ data::adt::{StructKind, VariantData},
+ hir::{
+ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
+ LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
},
lang_item::{LangItem, LangItemTarget},
- layout::LayoutError,
path::Path,
- resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
- DefWithBodyId, EnumVariantId, HasModule,
+ resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
+ AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
+ TraitId, TypeOrConstParamId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
+use rustc_hash::FxHashMap;
+use syntax::TextRange;
+use triomphe::Arc;
use crate::{
- consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
- inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
- utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+ consteval::ConstEvalError,
+ db::HirDatabase,
+ display::HirDisplay,
+ infer::{CaptureKind, CapturedItem, TypeMismatch},
+ inhabitedness::is_ty_uninhabited_from,
+ layout::LayoutError,
+ mapping::ToChalk,
+ static_lifetime,
+ traits::FnTrait,
+ utils::{generics, ClosureSubst},
+ Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
};
use super::*;
mod as_place;
+mod pattern_matching;
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
struct LoopBlocks {
begin: BasicBlockId,
/// `None` for loops that are not terminating
end: Option<BasicBlockId>,
+ place: Place,
+ drop_scope_index: usize,
+}
+
+#[derive(Debug, Clone, Default)]
+struct DropScope {
+ /// locals, in order of definition (so we should run drop glues in reverse order)
+ locals: Vec<LocalId>,
}
struct MirLowerCtx<'a> {
result: MirBody,
owner: DefWithBodyId,
current_loop_blocks: Option<LoopBlocks>,
+ labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
db: &'a dyn HirDatabase,
body: &'a Body,
infer: &'a InferenceResult,
+ drop_scopes: Vec<DropScope>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError {
- ConstEvalError(Box<ConstEvalError>),
+ ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
+ IncompletePattern,
+ /// Trying to lower a trait function, instead of an implementation
+ TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String),
RecordLiteralWithoutPath,
- UnresolvedMethod,
+ UnresolvedMethod(String),
UnresolvedField,
- MissingFunctionDefinition,
+ UnsizedTemporary(Ty),
+ MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch),
/// This should be never happen. Type mismatch should catch everything.
TypeError(&'static str),
@@ -63,9 +91,114 @@ pub enum MirLowerError {
BreakWithoutLoop,
Loop,
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
- ImplementationError(&'static str),
+ ImplementationError(String),
LangItemNotFound(LangItem),
MutatingRvalue,
+ UnresolvedLabel,
+ UnresolvedUpvar(Place),
+ UnaccessableLocal,
+
+ // monomorphization errors:
+ GenericArgNotProvided(TypeOrConstParamId, Substitution),
+}
+
+/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
+struct DropScopeToken;
+impl DropScopeToken {
+ fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_internal(current)
+ }
+
+ /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
+ /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled
+ /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't
+ /// do anything)
+ fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_assume_dropped_internal();
+ }
+}
+
+// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
+// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
+// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
+// stack trace.
+//
+// impl Drop for DropScopeToken {
+// fn drop(&mut self) {
+// never!("Drop scope doesn't popped");
+// }
+// }
+
+impl MirLowerError {
+ pub fn pretty_print(
+ &self,
+ f: &mut String,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ match self {
+ MirLowerError::ConstEvalError(name, e) => {
+ writeln!(f, "In evaluating constant {name}")?;
+ match &**e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
+ ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
+ }
+ }
+ MirLowerError::MissingFunctionDefinition(owner, x) => {
+ let body = db.body(*owner);
+ writeln!(
+ f,
+ "Missing function definition for {}",
+ body.pretty_print_expr(db.upcast(), *owner, *x)
+ )?;
+ }
+ MirLowerError::TypeMismatch(e) => {
+ writeln!(
+ f,
+ "Type mismatch: Expected {}, found {}",
+ e.expected.display(db),
+ e.actual.display(db),
+ )?;
+ }
+ MirLowerError::GenericArgNotProvided(id, subst) => {
+ let parent = id.parent;
+ let param = &db.generic_params(parent).type_or_consts[id.local_id];
+ writeln!(
+ f,
+ "Generic arg not provided for {}",
+ param.name().unwrap_or(&Name::missing()).display(db.upcast())
+ )?;
+ writeln!(f, "Provided args: [")?;
+ for g in subst.iter(Interner) {
+ write!(f, " {},", g.display(db).to_string())?;
+ }
+ writeln!(f, "]")?;
+ }
+ MirLowerError::LayoutError(_)
+ | MirLowerError::UnsizedTemporary(_)
+ | MirLowerError::IncompleteExpr
+ | MirLowerError::IncompletePattern
+ | MirLowerError::UnaccessableLocal
+ | MirLowerError::TraitFunctionDefinition(_, _)
+ | MirLowerError::UnresolvedName(_)
+ | MirLowerError::RecordLiteralWithoutPath
+ | MirLowerError::UnresolvedMethod(_)
+ | MirLowerError::UnresolvedField
+ | MirLowerError::TypeError(_)
+ | MirLowerError::NotSupported(_)
+ | MirLowerError::ContinueWithoutLoop
+ | MirLowerError::BreakWithoutLoop
+ | MirLowerError::Loop
+ | MirLowerError::ImplementationError(_)
+ | MirLowerError::LangItemNotFound(_)
+ | MirLowerError::MutatingRvalue
+ | MirLowerError::UnresolvedLabel
+ | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
+ }
+ Ok(())
+ }
}
macro_rules! not_supported {
@@ -76,20 +209,11 @@ macro_rules! not_supported {
macro_rules! implementation_error {
($x: expr) => {{
- ::stdx::never!("MIR lower implementation bug: {}", $x);
- return Err(MirLowerError::ImplementationError($x));
+ ::stdx::never!("MIR lower implementation bug: {}", format!($x));
+ return Err(MirLowerError::ImplementationError(format!($x)));
}};
}
-impl From<ConstEvalError> for MirLowerError {
- fn from(value: ConstEvalError) -> Self {
- match value {
- ConstEvalError::MirLowerError(e) => e,
- _ => MirLowerError::ConstEvalError(Box::new(value)),
- }
- }
-}
-
impl From<LayoutError> for MirLowerError {
fn from(value: LayoutError) -> Self {
MirLowerError::LayoutError(value)
@@ -104,12 +228,51 @@ impl MirLowerError {
type Result<T> = std::result::Result<T, MirLowerError>;
-impl MirLowerCtx<'_> {
- fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+impl<'ctx> MirLowerCtx<'ctx> {
+ fn new(
+ db: &'ctx dyn HirDatabase,
+ owner: DefWithBodyId,
+ body: &'ctx Body,
+ infer: &'ctx InferenceResult,
+ ) -> Self {
+ let mut basic_blocks = Arena::new();
+ let start_block = basic_blocks.alloc(BasicBlock {
+ statements: vec![],
+ terminator: None,
+ is_cleanup: false,
+ });
+ let locals = Arena::new();
+ let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+ let mir = MirBody {
+ basic_blocks,
+ locals,
+ start_block,
+ binding_locals,
+ param_locals: vec![],
+ owner,
+ closures: vec![],
+ };
+ let ctx = MirLowerCtx {
+ result: mir,
+ db,
+ infer,
+ body,
+ owner,
+ current_loop_blocks: None,
+ labeled_loop_blocks: Default::default(),
+ discr_temp: None,
+ drop_scopes: vec![DropScope::default()],
+ };
+ ctx
+ }
+
+ fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
- implementation_error!("unsized temporaries");
+ return Err(MirLowerError::UnsizedTemporary(ty));
}
- Ok(self.result.locals.alloc(Local { ty }))
+ let l = self.result.locals.alloc(Local { ty });
+ self.push_storage_live_for_local(l, current, span)?;
+ Ok(l)
}
fn lower_expr_to_some_operand(
@@ -120,7 +283,7 @@ impl MirLowerCtx<'_> {
if !self.has_adjustments(expr_id) {
match &self.body.exprs[expr_id] {
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
}
_ => (),
@@ -142,7 +305,8 @@ impl MirLowerCtx<'_> {
match adjustments.split_last() {
Some((last, rest)) => match &last.kind {
Adjust::NeverToAny => {
- let temp = self.temp(TyKind::Never.intern(Interner))?;
+ let temp =
+ self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?;
self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
}
Adjust::Deref(_) => {
@@ -200,65 +364,82 @@ impl MirLowerCtx<'_> {
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] {
- Expr::Missing => Err(MirLowerError::IncompleteExpr),
+ Expr::Missing => {
+ if let DefWithBodyId::FunctionId(f) = self.owner {
+ let assoc = self.db.lookup_intern_function(f);
+ if let ItemContainerId::TraitId(t) = assoc.container {
+ let name = &self.db.function_data(f).name;
+ return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
+ }
+ }
+ Err(MirLowerError::IncompleteExpr)
+ },
Expr::Path(p) => {
- let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let pr = resolver
- .resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
- .ok_or_else(unresolved_name)?;
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => {
- if let Some(assoc) = self
- .infer
- .assoc_resolutions_for_expr(expr_id)
- {
- match assoc.0 {
- hir_def::AssocItemId::ConstId(c) => {
- self.lower_const(c, current, place, expr_id.into())?;
- return Ok(Some(current))
- },
- _ => not_supported!("associated functions and types"),
- }
- } else if let Some(variant) = self
- .infer
- .variant_resolution_for_expr(expr_id)
- {
- match variant {
- VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
- VariantId::StructId(s) => ValueNs::StructId(s),
- VariantId::UnionId(_) => implementation_error!("Union variant as path"),
- }
- } else {
- return Err(unresolved_name());
+ let pr = if let Some((assoc, subst)) = self
+ .infer
+ .assoc_resolutions_for_expr(expr_id)
+ {
+ match assoc {
+ hir_def::AssocItemId::ConstId(c) => {
+ self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ return Ok(Some(current))
+ },
+ hir_def::AssocItemId::FunctionId(_) => {
+ // FnDefs are zero sized, no action is needed.
+ return Ok(Some(current))
}
+ hir_def::AssocItemId::TypeAliasId(_) => {
+ // FIXME: If it is unreachable, use proper error instead of `not_supported`.
+ not_supported!("associated functions and types")
+ },
}
+ } else if let Some(variant) = self
+ .infer
+ .variant_resolution_for_expr(expr_id)
+ {
+ match variant {
+ VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+ VariantId::StructId(s) => ValueNs::StructId(s),
+ VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+ }
+ } else {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+ resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?
};
match pr {
- ValueNs::LocalBinding(pat_id) => {
+ ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
+ let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else {
+ return Ok(None);
+ };
self.push_assignment(
current,
place,
- Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+ Operand::Copy(temp).into(),
expr_id.into(),
);
Ok(Some(current))
}
ValueNs::ConstId(const_id) => {
- self.lower_const(const_id, current, place, expr_id.into())?;
+ self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
Ok(Some(current))
}
ValueNs::EnumVariantId(variant_id) => {
- let ty = self.infer.type_of_expr[expr_id].clone();
- let current = self.lower_enum_variant(
- variant_id,
- current,
- place,
- ty,
- vec![],
- expr_id.into(),
- )?;
+ let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
+ if variant_data.variant_data.kind() == StructKind::Unit {
+ let ty = self.infer.type_of_expr[expr_id].clone();
+ current = self.lower_enum_variant(
+ variant_id,
+ current,
+ place,
+ ty,
+ Box::new([]),
+ expr_id.into(),
+ )?;
+ }
+ // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
Ok(Some(current))
}
ValueNs::GenericParam(p) => {
@@ -266,7 +447,7 @@ impl MirLowerCtx<'_> {
not_supported!("owner without generic def id");
};
let gen = generics(self.db.upcast(), def);
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
self.push_assignment(
current,
place,
@@ -287,7 +468,7 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- ValueNs::StructId(_) => {
+ ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
// It's probably a unit struct or a zero sized function, so no action is needed.
Ok(Some(current))
}
@@ -311,12 +492,13 @@ impl MirLowerCtx<'_> {
};
self.set_terminator(
current,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
},
+ expr_id.into(),
);
- Ok(self.merge_blocks(end_of_then, end_of_else))
+ Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
}
Expr::Let { pat, expr } => {
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
@@ -326,9 +508,7 @@ impl MirLowerCtx<'_> {
current,
None,
cond_place,
- self.expr_ty_after_adjustments(*expr),
*pat,
- BindingAnnotation::Unannotated,
)?;
self.write_bytes_to_place(
then_target,
@@ -346,141 +526,107 @@ impl MirLowerCtx<'_> {
MirSpan::Unknown,
)?;
}
- Ok(self.merge_blocks(Some(then_target), else_target))
+ Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
}
Expr::Unsafe { id: _, statements, tail } => {
- self.lower_block_to_place(None, statements, current, *tail, place)
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
}
Expr::Block { id: _, statements, tail, label } => {
- self.lower_block_to_place(*label, statements, current, *tail, place)
+ if let Some(label) = label {
+ self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
+ if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
+ let end = this.current_loop_end()?;
+ this.set_goto(current, end, expr_id.into());
+ }
+ Ok(())
+ })
+ } else {
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
+ }
}
- Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
- if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
- this.set_goto(block, begin);
+ Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
+ let scope = this.push_drop_scope();
+ if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
+ current = scope.pop_and_drop(this, current);
+ this.set_goto(current, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
}),
Expr::While { condition, body, label } => {
- self.lower_loop(current, *label, |this, begin| {
+ self.lower_loop(current, place, *label, expr_id.into(),|this, begin| {
+ let scope = this.push_drop_scope();
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
return Ok(());
};
- let end = this.current_loop_end()?;
+ let fail_cond = this.new_basic_block();
let after_cond = this.new_basic_block();
this.set_terminator(
to_switch,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
- targets: SwitchTargets::static_if(1, after_cond, end),
+ targets: SwitchTargets::static_if(1, after_cond, fail_cond),
},
+ expr_id.into(),
);
+ let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond);
+ let end = this.current_loop_end()?;
+ this.set_goto(fail_cond, end, expr_id.into());
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
- this.set_goto(block, begin);
+ let block = scope.pop_and_drop(this, block);
+ this.set_goto(block, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
})
}
- &Expr::For { iterable, pat, body, label } => {
- let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
- let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
- let option_some = self.resolve_lang_item(LangItem::OptionSome)?
- .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
- let option = option_some.parent;
- let into_iter_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let iter_next_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
- return Err(MirLowerError::TypeError("unknown for loop iterator type"));
- };
- let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
- let item_ty = &self.infer.type_of_pat[pat];
- let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
- let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
- let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
- let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
- let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
- else {
- return Ok(None);
- };
- self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
- self.lower_loop(current, label, |this, begin| {
- let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
- else {
- return Ok(());
- };
- let end = this.current_loop_end()?;
- let (current, _) = this.pattern_matching_variant(
- option_item_ty.clone(),
- BindingAnnotation::Unannotated,
- option_item_place.into(),
- option_some.into(),
- current,
- pat.into(),
- Some(end),
- &[pat], &None)?;
- if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
- this.set_goto(block, begin);
- }
- Ok(())
- })
- },
Expr::Call { callee, args, .. } => {
+ if let Some((func_id, generic_args)) =
+ self.infer.method_resolution(expr_id) {
+ let ty = chalk_ir::TyKind::FnDef(
+ CallableDefId::FunctionId(func_id).to_chalk(self.db),
+ generic_args,
+ )
+ .intern(Interner);
+ let func = Operand::from_bytes(vec![], ty);
+ return self.lower_call_and_args(
+ func,
+ iter::once(*callee).chain(args.iter().copied()),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
let callee_ty = self.expr_ty_after_adjustments(*callee);
match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => {
let func = Operand::from_bytes(vec![], callee_ty.clone());
- self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Scalar(_)
- | TyKind::Tuple(_, _)
- | TyKind::Array(_, _)
- | TyKind::Adt(_, _)
- | TyKind::Str
- | TyKind::Foreign(_)
- | TyKind::Slice(_) => {
- return Err(MirLowerError::TypeError("function call on data type"))
+ chalk_ir::TyKind::Function(_) => {
+ let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
+ return Ok(None);
+ };
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
- TyKind::AssociatedType(_, _)
- | TyKind::Raw(_, _)
- | TyKind::Ref(_, _, _)
- | TyKind::OpaqueType(_, _)
- | TyKind::Never
- | TyKind::Closure(_, _)
- | TyKind::Generator(_, _)
- | TyKind::GeneratorWitness(_, _)
- | TyKind::Placeholder(_)
- | TyKind::Dyn(_)
- | TyKind::Alias(_)
- | TyKind::Function(_)
- | TyKind::BoundVar(_)
- | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+ TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)),
+ _ => return Err(MirLowerError::TypeError("function call on bad type")),
}
}
- Expr::MethodCall { receiver, args, .. } => {
+ Expr::MethodCall { receiver, args, method_name, .. } => {
let (func_id, generic_args) =
- self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
- let ty = chalk_ir::TyKind::FnDef(
- CallableDefId::FunctionId(func_id).to_chalk(self.db),
- generic_args,
- )
- .intern(Interner);
- let func = Operand::from_bytes(vec![], ty);
+ self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?;
+ let func = Operand::from_fn(self.db, func_id, generic_args);
self.lower_call_and_args(
func,
iter::once(*receiver).chain(args.iter().copied()),
place,
current,
self.is_uninhabited(expr_id),
+ expr_id.into(),
)
}
Expr::Match { expr, arms } => {
@@ -488,23 +634,27 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- let cond_ty = self.expr_ty_after_adjustments(*expr);
let mut end = None;
for MatchArm { pat, guard, expr } in arms.iter() {
- if guard.is_some() {
- not_supported!("pattern matching with guard");
- }
- let (then, otherwise) = self.pattern_match(
+ let (then, mut otherwise) = self.pattern_match(
current,
None,
cond_place.clone(),
- cond_ty.clone(),
*pat,
- BindingAnnotation::Unannotated,
)?;
+ let then = if let &Some(guard) = guard {
+ let next = self.new_basic_block();
+ let o = otherwise.get_or_insert_with(|| self.new_basic_block());
+ if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
+ self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into());
+ }
+ next
+ } else {
+ then
+ };
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
let r = end.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(block, *r);
+ self.set_goto(block, *r, expr_id.into());
}
match otherwise {
Some(o) => current = o,
@@ -516,32 +666,43 @@ impl MirLowerCtx<'_> {
}
}
if self.is_unterminated(current) {
- self.set_terminator(current, Terminator::Unreachable);
+ self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
}
Ok(end)
}
- Expr::Continue { label } => match label {
- Some(_) => not_supported!("continue with label"),
- None => {
- let loop_data =
- self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
- self.set_goto(current, loop_data.begin);
- Ok(None)
- }
+ Expr::Continue { label } => {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
+ };
+ let begin = loop_data.begin;
+ current = self.drop_until_scope(loop_data.drop_scope_index, current);
+ self.set_goto(current, begin, expr_id.into());
+ Ok(None)
},
- Expr::Break { expr, label } => {
- if expr.is_some() {
- not_supported!("break with value");
+ &Expr::Break { expr, label } => {
+ if let Some(expr) = expr {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
+ };
+ let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else {
+ return Ok(None);
+ };
+ current = c;
}
- match label {
- Some(_) => not_supported!("break with label"),
+ let (end, drop_scope) = match label {
+ Some(l) => {
+ let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?;
+ (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index)
+ },
None => {
- let end =
- self.current_loop_end()?;
- self.set_goto(current, end);
- Ok(None)
- }
- }
+ (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index)
+ },
+ };
+ current = self.drop_until_scope(drop_scope, current);
+ self.set_goto(current, end, expr_id.into());
+ Ok(None)
}
Expr::Return { expr } => {
if let Some(expr) = expr {
@@ -551,11 +712,22 @@ impl MirLowerCtx<'_> {
return Ok(None);
}
}
- self.set_terminator(current, Terminator::Return);
+ current = self.drop_until_scope(0, current);
+ self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None)
}
Expr::Yield { .. } => not_supported!("yield"),
- Expr::RecordLit { fields, path, .. } => {
+ Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
+ let spread_place = match spread {
+ &Some(x) => {
+ let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else {
+ return Ok(None);
+ };
+ current = c;
+ Some(p)
+ },
+ None => None,
+ };
let variant_id = self
.infer
.variant_resolution_for_expr(expr_id)
@@ -563,7 +735,7 @@ impl MirLowerCtx<'_> {
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
None => MirLowerError::RecordLiteralWithoutPath,
})?;
- let subst = match self.expr_ty(expr_id).kind(Interner) {
+ let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
TyKind::Adt(_, s) => s.clone(),
_ => not_supported!("Non ADT record literal"),
};
@@ -585,9 +757,23 @@ impl MirLowerCtx<'_> {
place,
Rvalue::Aggregate(
AggregateKind::Adt(variant_id, subst),
- operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
+ match spread_place {
+ Some(sp) => operands.into_iter().enumerate().map(|(i, x)| {
+ match x {
+ Some(x) => x,
+ None => {
+ let p = sp.project(ProjectionElem::Field(FieldId {
+ parent: variant_id,
+ local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)),
+ }));
+ Operand::Copy(p)
+ },
+ }
+ }).collect(),
+ None => operands.into_iter().collect::<Option<_>>().ok_or(
+ MirLowerError::TypeError("missing field in record literal"),
+ )?,
+ },
),
expr_id.into(),
);
@@ -599,20 +785,19 @@ impl MirLowerCtx<'_> {
};
let local_id =
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
- let mut place = place;
- place
- .projection
- .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
+ let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
self.lower_expr_to_place(*expr, place, current)
}
}
}
Expr::Await { .. } => not_supported!("await"),
- Expr::Try { .. } => not_supported!("? operator"),
Expr::Yeet { .. } => not_supported!("yeet"),
- Expr::TryBlock { .. } => not_supported!("try block"),
Expr::Async { .. } => not_supported!("async block"),
- Expr::Const { .. } => not_supported!("anonymous const block"),
+ &Expr::Const(id) => {
+ let subst = self.placeholder_subst();
+ self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ Ok(Some(current))
+ },
Expr::Cast { expr, type_ref: _ } => {
let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
@@ -635,21 +820,30 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current))
}
- Expr::Box { .. } => not_supported!("box expression"),
- Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+ Expr::Box { expr } => {
+ let ty = self.expr_ty_after_adjustments(*expr);
+ self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into());
+ let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+ return Ok(None);
+ };
+ let p = place.project(ProjectionElem::Deref);
+ self.push_assignment(current, p, operand.into(), expr_id.into());
+ Ok(Some(current))
+ },
+ Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None);
};
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current))
}
- Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+ Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
};
let operation = match op {
- hir_def::expr::UnaryOp::Not => UnOp::Not,
- hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+ hir_def::hir::UnaryOp::Not => UnOp::Not,
+ hir_def::hir::UnaryOp::Neg => UnOp::Neg,
_ => unreachable!(),
};
self.push_assignment(
@@ -662,24 +856,93 @@ impl MirLowerCtx<'_> {
},
Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
- if let hir_def::expr::BinaryOp::Assignment { op } = op {
- if op.is_some() {
- not_supported!("assignment with arith op (like +=)");
+ let is_builtin = 'b: {
+ // Without adjust here is a hack. We assume that we know every possible adjustment
+ // for binary operator, and use without adjust to simplify our conditions.
+ let lhs_ty = self.expr_ty_without_adjust(*lhs);
+ let rhs_ty = self.expr_ty_without_adjust(*rhs);
+ if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) {
+ if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() {
+ break 'b true;
+ }
}
- let Some((lhs_place, current)) =
+ let builtin_inequal_impls = matches!(
+ op,
+ BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
+ );
+ lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls)
+ };
+ if !is_builtin {
+ if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
+ let func = Operand::from_fn(self.db, func_id, generic_args);
+ return self.lower_call_and_args(
+ func,
+ [*lhs, *rhs].into_iter(),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
+ }
+ if let hir_def::hir::BinaryOp::Assignment { op } = op {
+ if let Some(op) = op {
+ // last adjustment is `&mut` which we don't want it.
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(lhs)
+ .and_then(|x| x.split_last())
+ .map(|x| x.1)
+ .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
+ let Some((lhs_place, current)) =
+ self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op);
+ self.push_assignment(current, lhs_place, r_value, expr_id.into());
+ return Ok(Some(current));
+ } else {
+ let Some((lhs_place, current)) =
self.lower_expr_as_place(current, *lhs, false)?
- else {
- return Ok(None);
- };
- let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
- return Ok(None);
- };
- self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
- return Ok(Some(current));
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
+ return Ok(Some(current));
+ }
}
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
return Ok(None);
};
+ if let hir_def::hir::BinaryOp::LogicOp(op) = op {
+ let value_to_short = match op {
+ syntax::ast::LogicOp::And => 0,
+ syntax::ast::LogicOp::Or => 1,
+ };
+ let start_of_then = self.new_basic_block();
+ self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into());
+ let end_of_then = Some(start_of_then);
+ let start_of_else = self.new_basic_block();
+ let end_of_else =
+ self.lower_expr_to_place(*rhs, place, start_of_else)?;
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: lhs_op,
+ targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else),
+ },
+ expr_id.into(),
+ );
+ return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
+ }
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
return Ok(None);
};
@@ -688,13 +951,13 @@ impl MirLowerCtx<'_> {
place,
Rvalue::CheckedBinaryOp(
match op {
- hir_def::expr::BinaryOp::LogicOp(op) => match op {
- hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
- hir_def::expr::LogicOp::Or => BinOp::BitOr,
+ hir_def::hir::BinaryOp::LogicOp(op) => match op {
+ hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+ hir_def::hir::LogicOp::Or => BinOp::BitOr,
},
- hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+ hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
},
lhs_op,
rhs_op,
@@ -703,8 +966,96 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- Expr::Range { .. } => not_supported!("range"),
- Expr::Closure { .. } => not_supported!("closure"),
+ &Expr::Range { lhs, rhs, range_type: _ } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let Some((adt, subst)) = ty.as_adt() else {
+ return Err(MirLowerError::TypeError("Range type is not adt"));
+ };
+ let AdtId::StructId(st) = adt else {
+ return Err(MirLowerError::TypeError("Range type is not struct"));
+ };
+ let mut lp = None;
+ let mut rp = None;
+ if let Some(x) = lhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ lp = Some(o);
+ current = c;
+ }
+ if let Some(x) = rhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ rp = Some(o);
+ current = c;
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(
+ AggregateKind::Adt(st.into(), subst.clone()),
+ self.db.struct_data(st).variant_data.fields().iter().map(|x| {
+ let o = match x.1.name.as_str() {
+ Some("start") => lp.take(),
+ Some("end") => rp.take(),
+ Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())),
+ _ => None,
+ };
+ o.ok_or(MirLowerError::UnresolvedField)
+ }).collect::<Result<_>>()?,
+ ),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
+ Expr::Closure { .. } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let TyKind::Closure(id, _) = ty.kind(Interner) else {
+ not_supported!("closure with non closure type");
+ };
+ self.result.closures.push(*id);
+ let (captures, _) = self.infer.closure_info(id);
+ let mut operands = vec![];
+ for capture in captures.iter() {
+ let p = Place {
+ local: self.binding_local(capture.place.local)?,
+ projection: capture.place.projections.clone().into_iter().map(|x| {
+ match x {
+ ProjectionElem::Deref => ProjectionElem::Deref,
+ ProjectionElem::Field(x) => ProjectionElem::Field(x),
+ ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end },
+ ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
+ ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
+ ProjectionElem::Index(x) => match x { },
+ }
+ }).collect(),
+ };
+ match &capture.kind {
+ CaptureKind::ByRef(bk) => {
+ let placeholder_subst = self.placeholder_subst();
+ let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
+ let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into();
+ self.push_assignment(
+ current,
+ tmp.clone(),
+ Rvalue::Ref(bk.clone(), p),
+ capture.span,
+ );
+ operands.push(Operand::Move(tmp));
+ },
+ CaptureKind::ByValue => operands.push(Operand::Move(p)),
+ }
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs
.iter()
@@ -720,7 +1071,7 @@ impl MirLowerCtx<'_> {
return Ok(None);
};
let r = Rvalue::Aggregate(
- AggregateKind::Tuple(self.expr_ty(expr_id)),
+ AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
values,
);
self.push_assignment(current, place, r, expr_id.into());
@@ -728,7 +1079,7 @@ impl MirLowerCtx<'_> {
}
Expr::Array(l) => match l {
Array::ElementList { elements, .. } => {
- let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
+ let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
TyKind::Array(ty, _) => ty.clone(),
_ => {
return Err(MirLowerError::TypeError(
@@ -756,10 +1107,25 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, r, expr_id.into());
Ok(Some(current))
}
- Array::Repeat { .. } => not_supported!("array repeat"),
+ Array::Repeat { initializer, .. } => {
+ let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
+ return Ok(None);
+ };
+ let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
+ TyKind::Array(_, len) => len.clone(),
+ _ => {
+ return Err(MirLowerError::TypeError(
+ "Array repeat expression with non array type",
+ ))
+ }
+ };
+ let r = Rvalue::Repeat(init, len);
+ self.push_assignment(current, place, r, expr_id.into());
+ Ok(Some(current))
+ },
},
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
let op = self.lower_literal_to_operand(ty, l)?;
self.push_assignment(current, place, op.into(), expr_id.into());
Ok(Some(current))
@@ -768,17 +1134,25 @@ impl MirLowerCtx<'_> {
}
}
+ fn placeholder_subst(&mut self) -> Substitution {
+ let placeholder_subst = match self.owner.as_generic_def_id() {
+ Some(x) => TyBuilder::placeholder_subst(self.db, x),
+ None => Substitution::empty(Interner),
+ };
+ placeholder_subst
+ }
+
fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
if let Expr::Field { expr, name } = &self.body[expr_id] {
if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
- place.projection.push(ProjectionElem::TupleField(index))
+ *place = place.project(ProjectionElem::TupleOrClosureField(index))
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
- place.projection.push(ProjectionElem::Field(field));
+ *place = place.project(ProjectionElem::Field(field));
}
} else {
not_supported!("")
@@ -786,33 +1160,75 @@ impl MirLowerCtx<'_> {
Ok(())
}
+ fn lower_literal_or_const_to_operand(
+ &mut self,
+ ty: Ty,
+ loc: &LiteralOrConst,
+ ) -> Result<Operand> {
+ match loc {
+ LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
+ LiteralOrConst::Const(c) => {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, c);
+ let resolver = self.owner.resolver(self.db.upcast());
+ let pr = resolver
+ .resolve_path_in_value_ns(self.db.upcast(), c)
+ .ok_or_else(unresolved_name)?;
+ match pr {
+ ResolveValueResult::ValueNs(v) => {
+ if let ValueNs::ConstId(c) = v {
+ self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
+ } else {
+ not_supported!("bad path in range pattern");
+ }
+ }
+ ResolveValueResult::Partial(_, _) => {
+ not_supported!("associated constants in range pattern")
+ }
+ }
+ }
+ }
+ }
+
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
- let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+ let size = self
+ .db
+ .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())?
.size
.bytes_usize();
let bytes = match l {
- hir_def::expr::Literal::String(b) => {
+ hir_def::hir::Literal::String(b) => {
let b = b.as_bytes();
- let mut data = vec![];
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::ByteString(b) => {
- let mut data = vec![];
+ hir_def::hir::Literal::CString(b) => {
+ let b = b.as_bytes();
+ let bytes = b.iter().copied().chain(iter::once(0)).collect::<Vec<_>>();
+
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
+ data.extend(0usize.to_le_bytes());
+ data.extend(bytes.len().to_le_bytes());
+ let mut mm = MemoryMap::default();
+ mm.insert(0, bytes);
+ return Ok(Operand::from_concrete_const(data, mm, ty));
+ }
+ hir_def::hir::Literal::ByteString(b) => {
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
- hir_def::expr::Literal::Bool(b) => vec![*b as u8],
- hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Float(f, _) => match size {
+ hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+ hir_def::hir::Literal::Bool(b) => vec![*b as u8],
+ hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Float(f, _) => match size {
8 => f.into_f64().to_le_bytes().into(),
4 => f.into_f32().to_le_bytes().into(),
_ => {
@@ -829,24 +1245,34 @@ impl MirLowerCtx<'_> {
fn lower_const(
&mut self,
- const_id: hir_def::ConstId,
+ const_id: GeneralConstId,
prev_block: BasicBlockId,
place: Place,
+ subst: Substitution,
span: MirSpan,
+ ty: Ty,
) -> Result<()> {
- let c = self.db.const_eval(const_id)?;
- self.write_const_to_place(c, prev_block, place, span)
+ let c = self.lower_const_to_operand(subst, const_id, ty)?;
+ self.push_assignment(prev_block, place, c.into(), span);
+ Ok(())
}
- fn write_const_to_place(
+ fn lower_const_to_operand(
&mut self,
- c: Const,
- prev_block: BasicBlockId,
- place: Place,
- span: MirSpan,
- ) -> Result<()> {
- self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
- Ok(())
+ subst: Substitution,
+ const_id: GeneralConstId,
+ ty: Ty,
+ ) -> Result<Operand> {
+ let c = if subst.len(Interner) != 0 {
+ // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
+ intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
+ } else {
+ let name = const_id.name(self.db.upcast());
+ self.db
+ .const_eval(const_id.into(), subst)
+ .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
+ };
+ Ok(Operand::Constant(c))
}
fn write_bytes_to_place(
@@ -867,12 +1293,12 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId,
place: Place,
ty: Ty,
- fields: Vec<Operand>,
+ fields: Box<[Operand]>,
span: MirSpan,
) -> Result<BasicBlockId> {
let subst = match ty.kind(Interner) {
TyKind::Adt(_, subst) => subst.clone(),
- _ => not_supported!("Non ADT enum"),
+ _ => implementation_error!("Non ADT enum"),
};
self.push_assignment(
prev_block,
@@ -890,6 +1316,7 @@ impl MirLowerCtx<'_> {
place: Place,
mut current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let Some(args) = args
.map(|arg| {
@@ -904,21 +1331,22 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- self.lower_call(func, args, place, current, is_uninhabited)
+ self.lower_call(func, args.into(), place, current, is_uninhabited, span)
}
fn lower_call(
&mut self,
func: Operand,
- args: Vec<Operand>,
+ args: Box<[Operand]>,
place: Place,
current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
self.set_terminator(
current,
- Terminator::Call {
+ TerminatorKind::Call {
func,
args,
destination: place,
@@ -926,6 +1354,7 @@ impl MirLowerCtx<'_> {
cleanup: None,
from_hir_call: true,
},
+ span,
);
Ok(b)
}
@@ -934,15 +1363,15 @@ impl MirLowerCtx<'_> {
self.result.basic_blocks[source].terminator.is_none()
}
- fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
- self.result.basic_blocks[source].terminator = Some(terminator);
+ fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
+ self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
}
- fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
- self.set_terminator(source, Terminator::Goto { target });
+ fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
+ self.set_terminator(source, TerminatorKind::Goto { target }, span);
}
- fn expr_ty(&self, e: ExprId) -> Ty {
+ fn expr_ty_without_adjust(&self, e: ExprId) -> Ty {
self.infer[e].clone()
}
@@ -953,7 +1382,7 @@ impl MirLowerCtx<'_> {
ty = Some(x.target.clone());
}
}
- ty.unwrap_or_else(|| self.expr_ty(e))
+ ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
}
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
@@ -970,293 +1399,14 @@ impl MirLowerCtx<'_> {
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
}
- /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
- /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
- /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
- /// mismatched path block is `None`.
- ///
- /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
- /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
- /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
- /// so it should be an empty block.
- fn pattern_match(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- mut cond_place: Place,
- mut cond_ty: Ty,
- pattern: PatId,
- mut binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- Ok(match &self.body.pats[pattern] {
- Pat::Missing => return Err(MirLowerError::IncompleteExpr),
- Pat::Wild => (current, current_else),
- Pat::Tuple { args, ellipsis } => {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Tuple(_, s) => s,
- _ => {
- return Err(MirLowerError::TypeError(
- "non tuple type matched with tuple pattern",
- ))
- }
- };
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().enumerate().map(|(i, x)| {
- (
- PlaceElem::TupleField(i),
- *x,
- subst.at(Interner, i).assert_ty_ref(Interner).clone(),
- )
- }),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- Pat::Or(pats) => {
- let then_target = self.new_basic_block();
- let mut finished = false;
- for pat in &**pats {
- let (next, next_else) = self.pattern_match(
- current,
- None,
- cond_place.clone(),
- cond_ty.clone(),
- *pat,
- binding_mode,
- )?;
- self.set_goto(next, then_target);
- match next_else {
- Some(t) => {
- current = t;
- }
- None => {
- finished = true;
- break;
- }
- }
- }
- if !finished {
- let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(current, ce);
- }
- (then_target, current_else)
- }
- Pat::Record { .. } => not_supported!("record pattern"),
- Pat::Range { .. } => not_supported!("range pattern"),
- Pat::Slice { .. } => not_supported!("slice pattern"),
- Pat::Path(_) => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- &[],
- &None,
- )?
- }
- Pat::Lit(l) => {
- let then_target = self.new_basic_block();
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- match &self.body.exprs[*l] {
- Expr::Literal(l) => match l {
- hir_def::expr::Literal::Int(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(
- *x as u128,
- then_target,
- else_target,
- ),
- },
- );
- }
- hir_def::expr::Literal::Uint(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(*x, then_target, else_target),
- },
- );
- }
- _ => not_supported!("non int path literal"),
- },
- _ => not_supported!("expression path literal"),
- }
- (then_target, Some(else_target))
- }
- Pat::Bind { id, subpat } => {
- let target_place = self.result.binding_locals[*id];
- let mode = self.body.bindings[*id].mode;
- if let Some(subpat) = subpat {
- (current, current_else) = self.pattern_match(
- current,
- current_else,
- cond_place.clone(),
- cond_ty,
- *subpat,
- binding_mode,
- )?
- }
- if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
- binding_mode = mode;
- }
- self.push_storage_live(*id, current);
- self.push_assignment(
- current,
- target_place.into(),
- match binding_mode {
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
- Operand::Copy(cond_place).into()
- }
- BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
- BindingAnnotation::RefMut => Rvalue::Ref(
- BorrowKind::Mut { allow_two_phase_borrow: false },
- cond_place,
- ),
- },
- pattern.into(),
- );
- (current, current_else)
- }
- Pat::TupleStruct { path: _, args, ellipsis } => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- args,
- ellipsis,
- )?
- }
- Pat::Ref { .. } => not_supported!("& pattern"),
- Pat::Box { .. } => not_supported!("box pattern"),
- Pat::ConstBlock(_) => not_supported!("const block pattern"),
- })
- }
-
- fn pattern_matching_variant(
- &mut self,
- mut cond_ty: Ty,
- mut binding_mode: BindingAnnotation,
- mut cond_place: Place,
- variant: VariantId,
- current: BasicBlockId,
- span: MirSpan,
- current_else: Option<BasicBlockId>,
- args: &[PatId],
- ellipsis: &Option<usize>,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Adt(_, s) => s,
- _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
- };
- let fields_type = self.db.field_types(variant);
- Ok(match variant {
- VariantId::EnumVariantId(v) => {
- let e = self.db.const_eval_discriminant(v)? as u128;
- let next = self.new_basic_block();
- let tmp = self.discr_temp_place();
- self.push_assignment(
- current,
- tmp.clone(),
- Rvalue::Discriminant(cond_place.clone()),
- span,
- );
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(tmp),
- targets: SwitchTargets::static_if(e, next, else_target),
- },
- );
- let enum_data = self.db.enum_data(v.parent);
- let fields =
- enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- next,
- Some(else_target),
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::StructId(s) => {
- let struct_data = self.db.struct_data(s);
- let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::UnionId(_) => {
- return Err(MirLowerError::TypeError("pattern matching on union"))
- }
- })
- }
-
- fn pattern_match_tuple_like(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
- ellipsis: Option<usize>,
- cond_place: &Place,
- binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- if ellipsis.is_some() {
- not_supported!("tuple like pattern with ellipsis");
- }
- for (proj, arg, ty) in args {
- let mut cond_place = cond_place.clone();
- cond_place.projection.push(proj);
- (current, current_else) =
- self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
- }
- Ok((current, current_else))
- }
-
- fn discr_temp_place(&mut self) -> Place {
+ fn discr_temp_place(&mut self, current: BasicBlockId) -> Place {
match &self.discr_temp {
Some(x) => x.clone(),
None => {
- let tmp: Place =
- self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+ let tmp: Place = self
+ .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown)
+ .expect("discr_ty is never unsized")
+ .into();
self.discr_temp = Some(tmp.clone());
tmp
}
@@ -1266,19 +1416,34 @@ impl MirLowerCtx<'_> {
fn lower_loop(
&mut self,
prev_block: BasicBlockId,
+ place: Place,
label: Option<LabelId>,
+ span: MirSpan,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> {
- if label.is_some() {
- not_supported!("loop with label");
- }
let begin = self.new_basic_block();
- let prev =
- mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
- self.set_goto(prev_block, begin);
+ let prev = mem::replace(
+ &mut self.current_loop_blocks,
+ Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }),
+ );
+ let prev_label = if let Some(label) = label {
+ // We should generate the end now, to make sure that it wouldn't change later. It is
+ // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but
+ // it should not affect correctness.
+ self.current_loop_end()?;
+ self.labeled_loop_blocks
+ .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
+ } else {
+ None
+ };
+ self.set_goto(prev_block, begin, span);
f(self, begin)?;
- let my = mem::replace(&mut self.current_loop_blocks, prev)
- .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+ let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
+ MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
+ )?;
+ if let Some(prev) = prev_label {
+ self.labeled_loop_blocks.insert(label.unwrap(), prev);
+ }
Ok(my.end)
}
@@ -1290,14 +1455,15 @@ impl MirLowerCtx<'_> {
&mut self,
b1: Option<BasicBlockId>,
b2: Option<BasicBlockId>,
+ span: MirSpan,
) -> Option<BasicBlockId> {
match (b1, b2) {
(None, None) => None,
(None, Some(b)) | (Some(b), None) => Some(b),
(Some(b1), Some(b2)) => {
let bm = self.new_basic_block();
- self.set_goto(b1, bm);
- self.set_goto(b2, bm);
+ self.set_goto(b1, bm, span);
+ self.set_goto(b2, bm, span);
Some(bm)
}
}
@@ -1307,7 +1473,9 @@ impl MirLowerCtx<'_> {
let r = match self
.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end
{
Some(x) => x,
@@ -1315,7 +1483,9 @@ impl MirLowerCtx<'_> {
let s = self.new_basic_block();
self.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end = Some(s);
s
}
@@ -1327,36 +1497,28 @@ impl MirLowerCtx<'_> {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
}
- /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
- /// the appropriated places.
- fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
- // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
- // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
- // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
- // allow this:
- //
- // ```
- // let x;
- // loop {
- // let y = 2;
- // x = &y;
- // if some_condition {
- // break; // we need to add a StorageDead(y) above this to kill the x borrow
- // }
- // }
- // use(x)
- // ```
- // But I think this approach work for mutability analysis, as user can't write code which mutates a binding
- // after StorageDead, except loops, which are handled by this hack.
+ /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
+ /// `Drop` in the appropriated places.
+ fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
let span = self.body.bindings[b]
.definitions
.first()
.copied()
.map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown);
- let l = self.result.binding_locals[b];
- self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
+ let l = self.binding_local(b)?;
+ self.push_storage_live_for_local(l, current, span)
+ }
+
+ fn push_storage_live_for_local(
+ &mut self,
+ l: LocalId,
+ current: BasicBlockId,
+ span: MirSpan,
+ ) -> Result<()> {
+ self.drop_scopes.last_mut().unwrap().locals.push(l);
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+ Ok(())
}
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@@ -1366,81 +1528,204 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place(
&mut self,
- label: Option<LabelId>,
- statements: &[hir_def::expr::Statement],
+ statements: &[hir_def::hir::Statement],
mut current: BasicBlockId,
tail: Option<ExprId>,
place: Place,
+ span: MirSpan,
) -> Result<Option<Idx<BasicBlock>>> {
- if label.is_some() {
- not_supported!("block with label");
- }
+ let scope = self.push_drop_scope();
for statement in statements.iter() {
match statement {
- hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+ hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
if let Some(expr_id) = initializer {
let else_block;
let Some((init_place, c)) =
self.lower_expr_as_place(current, *expr_id, true)?
else {
+ scope.pop_assume_dropped(self);
return Ok(None);
};
current = c;
- (current, else_block) = self.pattern_match(
- current,
- None,
- init_place,
- self.expr_ty_after_adjustments(*expr_id),
- *pat,
- BindingAnnotation::Unannotated,
- )?;
+ (current, else_block) =
+ self.pattern_match(current, None, init_place, *pat)?;
match (else_block, else_branch) {
(None, _) => (),
(Some(else_block), None) => {
- self.set_terminator(else_block, Terminator::Unreachable);
+ self.set_terminator(else_block, TerminatorKind::Unreachable, span);
}
(Some(else_block), Some(else_branch)) => {
if let Some((_, b)) =
self.lower_expr_as_place(else_block, *else_branch, true)?
{
- self.set_terminator(b, Terminator::Unreachable);
+ self.set_terminator(b, TerminatorKind::Unreachable, span);
}
}
}
} else {
+ let mut err = None;
self.body.walk_bindings_in_pat(*pat, |b| {
- self.push_storage_live(b, current);
+ if let Err(e) = self.push_storage_live(b, current) {
+ err = Some(e);
+ }
});
+ if let Some(e) = err {
+ return Err(e);
+ }
}
}
- hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+ hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
+ let scope2 = self.push_drop_scope();
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
+ scope2.pop_assume_dropped(self);
+ scope.pop_assume_dropped(self);
return Ok(None);
};
- current = c;
+ current = scope2.pop_and_drop(self, c);
}
}
}
- match tail {
- Some(tail) => self.lower_expr_to_place(tail, place, current),
- None => Ok(Some(current)),
+ if let Some(tail) = tail {
+ let Some(c) = self.lower_expr_to_place(tail, place, current)? else {
+ scope.pop_assume_dropped(self);
+ return Ok(None);
+ };
+ current = c;
}
+ current = scope.pop_and_drop(self, current);
+ Ok(Some(current))
}
-}
-fn pattern_matching_dereference(
- cond_ty: &mut Ty,
- binding_mode: &mut BindingAnnotation,
- cond_place: &mut Place,
-) {
- while let Some((ty, _, mu)) = cond_ty.as_reference() {
- if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
- *binding_mode = BindingAnnotation::RefMut;
- } else {
- *binding_mode = BindingAnnotation::Ref;
+ fn lower_params_and_bindings(
+ &mut self,
+ params: impl Iterator<Item = (PatId, Ty)> + Clone,
+ pick_binding: impl Fn(BindingId) -> bool,
+ ) -> Result<BasicBlockId> {
+ let base_param_count = self.result.param_locals.len();
+ self.result.param_locals.extend(params.clone().map(|(x, ty)| {
+ let local_id = self.result.locals.alloc(Local { ty });
+ self.drop_scopes.last_mut().unwrap().locals.push(local_id);
+ if let Pat::Bind { id, subpat: None } = self.body[x] {
+ if matches!(
+ self.body.bindings[id].mode,
+ BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+ ) {
+ self.result.binding_locals.insert(id, local_id);
+ }
+ }
+ local_id
+ }));
+ // and then rest of bindings
+ for (id, _) in self.body.bindings.iter() {
+ if !pick_binding(id) {
+ continue;
+ }
+ if !self.result.binding_locals.contains_idx(id) {
+ self.result
+ .binding_locals
+ .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
+ }
+ }
+ let mut current = self.result.start_block;
+ for ((param, _), local) in
+ params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
+ {
+ if let Pat::Bind { id, .. } = self.body[param] {
+ if local == self.binding_local(id)? {
+ continue;
+ }
+ }
+ let r = self.pattern_match(current, None, local.into(), param)?;
+ if let Some(b) = r.1 {
+ self.set_terminator(b, TerminatorKind::Unreachable, param.into());
+ }
+ current = r.0;
+ }
+ Ok(current)
+ }
+
+ fn binding_local(&self, b: BindingId) -> Result<LocalId> {
+ match self.result.binding_locals.get(b) {
+ Some(x) => Ok(*x),
+ None => {
+ // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which
+ // is a hir lowering problem IMO.
+ // never!("Using unaccessable local for binding is always a bug");
+ Err(MirLowerError::UnaccessableLocal)
+ }
+ }
+ }
+
+ fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
+ let r = self.db.const_eval_discriminant(variant);
+ match r {
+ Ok(r) => Ok(r),
+ Err(e) => {
+ let data = self.db.enum_data(variant.parent);
+ let name = format!(
+ "{}::{}",
+ data.name.display(self.db.upcast()),
+ data.variants[variant.local_id].name.display(self.db.upcast())
+ );
+ Err(MirLowerError::ConstEvalError(name, Box::new(e)))
+ }
+ }
+ }
+
+ fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
+ for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
+ self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
+ }
+ current
+ }
+
+ fn push_drop_scope(&mut self) -> DropScopeToken {
+ self.drop_scopes.push(DropScope::default());
+ DropScopeToken
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_assume_dropped_internal(&mut self) {
+ self.drop_scopes.pop();
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
+ let scope = self.drop_scopes.pop().unwrap();
+ self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
+ current
+ }
+
+ fn pop_drop_scope_assert_finished(
+ &mut self,
+ mut current: BasicBlockId,
+ ) -> Result<BasicBlockId> {
+ current = self.pop_drop_scope_internal(current);
+ if !self.drop_scopes.is_empty() {
+ implementation_error!("Mismatched count between drop scope push and pops");
+ }
+ Ok(current)
+ }
+
+ fn emit_drop_and_storage_dead_for_scope(
+ &mut self,
+ scope: &DropScope,
+ current: &mut Idx<BasicBlock>,
+ ) {
+ for &l in scope.locals.iter().rev() {
+ if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
+ let prev = std::mem::replace(current, self.new_basic_block());
+ self.set_terminator(
+ prev,
+ TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
+ MirSpan::Unknown,
+ );
+ }
+ self.push_statement(
+ *current,
+ StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
+ );
}
- *cond_ty = ty.clone();
- cond_place.projection.push(ProjectionElem::Deref);
}
}
@@ -1452,6 +1737,26 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
(_, _) => CastKind::IntToInt,
},
+ (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
+ (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
+ (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
+ CastKind::Pointer(if a == b {
+ PointerCast::MutToConstPointer
+ } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ {
+ // slice to slice cast is no-op (metadata is not touched), so we use this
+ PointerCast::MutToConstPointer
+ } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+ PointerCast::Unsize
+ } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
+ PointerCast::ArrayToPointer
+ } else {
+ // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
+ // for it in `PointerCast` so we use `MutToConstPointer`
+ PointerCast::MutToConstPointer
+ })
+ }
// Enum to int casts
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
CastKind::IntToInt
@@ -1460,20 +1765,123 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
})
}
+pub fn mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+) -> Result<Arc<MirBody>> {
+ let (owner, expr) = db.lookup_intern_closure(closure.into());
+ let body = db.body(owner);
+ let infer = db.infer(owner);
+ let Expr::Closure { args, body: root, .. } = &body[expr] else {
+ implementation_error!("closure expression is not closure");
+ };
+ let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
+ implementation_error!("closure expression is not closure");
+ };
+ let (captures, kind) = infer.closure_info(&closure);
+ let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
+ // 0 is return local
+ ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
+ let closure_local = ctx.result.locals.alloc(Local {
+ ty: match kind {
+ FnTrait::FnOnce => infer[expr].clone(),
+ FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ },
+ });
+ ctx.result.param_locals.push(closure_local);
+ let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
+ implementation_error!("closure has not callable sig");
+ };
+ let current = ctx.lower_params_and_bindings(
+ args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
+ |_| true,
+ )?;
+ if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
+ }
+ let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
+ for (i, capture) in captures.iter().enumerate() {
+ let local = ctx.binding_local(capture.place.local)?;
+ upvar_map.entry(local).or_default().push((capture, i));
+ }
+ let mut err = None;
+ let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
+ let closure_projection = match kind {
+ FnTrait::FnOnce => vec![],
+ FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
+ };
+ ctx.result.walk_places(|p| {
+ if let Some(x) = upvar_map.get(&p.local) {
+ let r = x.iter().find(|x| {
+ if p.projection.len() < x.0.place.projections.len() {
+ return false;
+ }
+ for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
+ match (x, y) {
+ (ProjectionElem::Deref, ProjectionElem::Deref) => (),
+ (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
+ (
+ ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::TupleOrClosureField(y),
+ ) if x == y => (),
+ _ => return false,
+ }
+ }
+ true
+ });
+ match r {
+ Some(x) => {
+ p.local = closure_local;
+ let mut next_projs = closure_projection.clone();
+ next_projs.push(PlaceElem::TupleOrClosureField(x.1));
+ let prev_projs = mem::take(&mut p.projection);
+ if x.0.kind != CaptureKind::ByValue {
+ next_projs.push(ProjectionElem::Deref);
+ }
+ next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len()));
+ p.projection = next_projs.into();
+ }
+ None => err = Some(p.clone()),
+ }
+ }
+ });
+ ctx.result.binding_locals = ctx
+ .result
+ .binding_locals
+ .into_iter()
+ .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
+ .collect();
+ if let Some(err) = err {
+ return Err(MirLowerError::UnresolvedUpvar(err));
+ }
+ ctx.result.shrink_to_fit();
+ Ok(Arc::new(ctx.result))
+}
+
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
let _p = profile::span("mir_body_query").detail(|| match def {
- DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
- DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
- DefWithBodyId::ConstId(it) => {
- db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
- }
+ DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::ConstId(it) => db
+ .const_data(it)
+ .name
+ .clone()
+ .unwrap_or_else(Name::missing)
+ .display(db.upcast())
+ .to_string(),
DefWithBodyId::VariantId(it) => {
- db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
}
+ DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
});
let body = db.body(def);
let infer = db.infer(def);
- let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ result.shrink_to_fit();
Ok(Arc::new(result))
}
@@ -1497,85 +1905,40 @@ pub fn lower_to_mir(
if let Some((_, x)) = infer.type_mismatches().next() {
return Err(MirLowerError::TypeMismatch(x.clone()));
}
- let mut basic_blocks = Arena::new();
- let start_block =
- basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
- let mut locals = Arena::new();
+ let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local
- locals.alloc(Local { ty: infer[root_expr].clone() });
- let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
- // 1 to param_len is for params
- let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
- let substs = TyBuilder::placeholder_subst(db, fid);
- let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
- body.params
- .iter()
- .zip(callable_sig.params().iter())
- .map(|(&x, ty)| {
- let local_id = locals.alloc(Local { ty: ty.clone() });
- if let Pat::Bind { id, subpat: None } = body[x] {
- if matches!(
- body.bindings[id].mode,
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable
- ) {
- binding_locals.insert(id, local_id);
- }
- }
- local_id
- })
- .collect()
- } else {
- if !body.params.is_empty() {
- return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
- }
- vec![]
- };
- // and then rest of bindings
- for (id, _) in body.bindings.iter() {
- if !binding_locals.contains_idx(id) {
- binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
+ ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
+ let binding_picker = |b: BindingId| {
+ let owner = ctx.body.binding_owners.get(&b).copied();
+ if root_expr == body.body_expr {
+ owner.is_none()
+ } else {
+ owner == Some(root_expr)
}
- }
- let mir = MirBody {
- basic_blocks,
- locals,
- start_block,
- binding_locals,
- param_locals,
- owner,
- arg_count: body.params.len(),
- };
- let mut ctx = MirLowerCtx {
- result: mir,
- db,
- infer,
- body,
- owner,
- current_loop_blocks: None,
- discr_temp: None,
};
- let mut current = start_block;
- for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
- if let Pat::Bind { id, .. } = body[param] {
- if local == ctx.result.binding_locals[id] {
- continue;
+ // 1 to param_len is for params
+ // FIXME: replace with let chain once it becomes stable
+ let current = 'b: {
+ if body.body_expr == root_expr {
+ // otherwise it's an inline const, and has no parameter
+ if let DefWithBodyId::FunctionId(fid) = owner {
+ let substs = TyBuilder::placeholder_subst(db, fid);
+ let callable_sig =
+ db.callable_item_signature(fid.into()).substitute(Interner, &substs);
+ break 'b ctx.lower_params_and_bindings(
+ body.params
+ .iter()
+ .zip(callable_sig.params().iter())
+ .map(|(x, y)| (*x, y.clone())),
+ binding_picker,
+ )?;
}
}
- let r = ctx.pattern_match(
- current,
- None,
- local.into(),
- ctx.result.locals[local].ty.clone(),
- param,
- BindingAnnotation::Unannotated,
- )?;
- if let Some(b) = r.1 {
- ctx.set_terminator(b, Terminator::Unreachable);
- }
- current = r.0;
- }
- if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
- ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+ ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
+ };
+ if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
}
Ok(ctx.result)
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index fe8147dcd..d2c8d9a08 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,6 +1,7 @@
//! MIR lowering for places
use super::*;
+use hir_def::{lang_item::lang_attr, FunctionId};
use hir_expand::name;
macro_rules! not_supported {
@@ -15,8 +16,8 @@ impl MirLowerCtx<'_> {
expr_id: ExprId,
prev_block: BasicBlockId,
) -> Result<Option<(Place, BasicBlockId)>> {
- let ty = self.expr_ty(expr_id);
- let place = self.temp(ty)?;
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
return Ok(None);
};
@@ -29,9 +30,11 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId,
adjustments: &[Adjustment],
) -> Result<Option<(Place, BasicBlockId)>> {
- let ty =
- adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
- let place = self.temp(ty)?;
+ let ty = adjustments
+ .last()
+ .map(|x| x.target.clone())
+ .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
+ let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
return Ok(None);
};
@@ -62,7 +65,7 @@ impl MirLowerCtx<'_> {
)? else {
return Ok(None);
};
- x.0.projection.push(ProjectionElem::Deref);
+ x.0 = x.0.project(ProjectionElem::Deref);
Ok(Some(x))
}
Adjust::Deref(Some(od)) => {
@@ -79,7 +82,7 @@ impl MirLowerCtx<'_> {
r,
rest.last()
.map(|x| x.target.clone())
- .unwrap_or_else(|| self.expr_ty(expr_id)),
+ .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
last.target.clone(),
expr_id.into(),
match od.0 {
@@ -125,35 +128,74 @@ impl MirLowerCtx<'_> {
match &self.body.exprs[expr_id] {
Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
- return Err(MirLowerError::unresolved_path(self.db, p));
- };
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => return try_rvalue(self),
+ let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else {
+ return try_rvalue(self);
};
match pr {
ValueNs::LocalBinding(pat_id) => {
- Ok(Some((self.result.binding_locals[pat_id].into(), current)))
+ Ok(Some((self.binding_local(pat_id)?.into(), current)))
+ }
+ ValueNs::StaticId(s) => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let ref_ty =
+ TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner);
+ let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into();
+ self.push_assignment(
+ current,
+ temp.clone(),
+ Operand::Static(s).into(),
+ expr_id.into(),
+ );
+ Ok(Some((temp.project(ProjectionElem::Deref), current)))
}
_ => try_rvalue(self),
}
}
Expr::UnaryOp { expr, op } => match op {
- hir_def::expr::UnaryOp::Deref => {
- if !matches!(
- self.expr_ty(*expr).kind(Interner),
- TyKind::Ref(..) | TyKind::Raw(..)
- ) {
- let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+ hir_def::hir::UnaryOp::Deref => {
+ let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
+ TyKind::Ref(..) | TyKind::Raw(..) => true,
+ TyKind::Adt(id, _) => {
+ if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
+ lang_item == LangItem::OwnedBox
+ } else {
+ false
+ }
+ }
+ _ => false,
+ };
+ if !is_builtin {
+ let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- not_supported!("explicit overloaded deref");
+ return self.lower_overloaded_deref(
+ current,
+ p,
+ self.expr_ty_after_adjustments(*expr),
+ self.expr_ty_without_adjust(expr_id),
+ expr_id.into(),
+ 'b: {
+ if let Some((f, _)) = self.infer.method_resolution(expr_id) {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
+ {
+ if let Some(deref_fn) = self
+ .db
+ .trait_data(deref_trait)
+ .method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ }
+ false
+ },
+ );
}
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- r.projection.push(ProjectionElem::Deref);
+ r = r.project(ProjectionElem::Deref);
Ok(Some((r, current)))
}
_ => try_rvalue(self),
@@ -169,25 +211,84 @@ impl MirLowerCtx<'_> {
let base_ty = self.expr_ty_after_adjustments(*base);
let index_ty = self.expr_ty_after_adjustments(*index);
if index_ty != TyBuilder::usize()
- || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
+ || !matches!(
+ base_ty.strip_reference().kind(Interner),
+ TyKind::Array(..) | TyKind::Slice(..)
+ )
{
- not_supported!("overloaded index");
+ let Some(index_fn) = self.infer.method_resolution(expr_id) else {
+ return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string()));
+ };
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+ let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
+ return Ok(None);
+ };
+ return self.lower_overloaded_index(
+ current,
+ base_place,
+ base_ty,
+ self.expr_ty_without_adjust(expr_id),
+ index_operand,
+ expr_id.into(),
+ index_fn,
+ );
}
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(base)
+ .and_then(|x| x.split_last())
+ .map(|x| x.1)
+ .unwrap_or(&[]);
let Some((mut p_base, current)) =
- self.lower_expr_as_place(current, *base, true)? else {
+ self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)?
+ else {
return Ok(None);
};
- let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
+ let l_index =
+ self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?;
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
return Ok(None);
};
- p_base.projection.push(ProjectionElem::Index(l_index));
+ p_base = p_base.project(ProjectionElem::Index(l_index));
Ok(Some((p_base, current)))
}
_ => try_rvalue(self),
}
}
+ fn lower_overloaded_index(
+ &mut self,
+ current: BasicBlockId,
+ place: Place,
+ base_ty: Ty,
+ result_ty: Ty,
+ index_operand: Operand,
+ span: MirSpan,
+ index_fn: (FunctionId, Substitution),
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let mutability = match base_ty.as_reference() {
+ Some((_, _, mutability)) => mutability,
+ None => Mutability::Not,
+ };
+ let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
+ let mut result: Place = self.temp(result_ref, current, span)?.into();
+ let index_fn_op = Operand::const_zst(
+ TyKind::FnDef(
+ self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
+ index_fn.1,
+ )
+ .intern(Interner),
+ );
+ let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else {
+ return Ok(None);
+ };
+ result = result.project(ProjectionElem::Deref);
+ Ok(Some((result, current)))
+ }
+
fn lower_overloaded_deref(
&mut self,
current: BasicBlockId,
@@ -209,7 +310,7 @@ impl MirLowerCtx<'_> {
};
let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
- let ref_place: Place = self.temp(ty_ref)?.into();
+ let ref_place: Place = self.temp(ty_ref, current, span)?.into();
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
let deref_trait = self
.resolve_lang_item(trait_lang_item)?
@@ -227,11 +328,11 @@ impl MirLowerCtx<'_> {
)
.intern(Interner),
);
- let mut result: Place = self.temp(target_ty_ref)?.into();
- let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
+ let mut result: Place = self.temp(target_ty_ref, current, span)?.into();
+ let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else {
return Ok(None);
};
- result.projection.push(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref);
Ok(Some((result, current)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
new file mode 100644
index 000000000..ff43c64a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -0,0 +1,617 @@
+//! MIR lowering for patterns
+
+use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
+
+use crate::BindingMode;
+
+use super::*;
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+pub(super) enum AdtPatternShape<'a> {
+ Tuple { args: &'a [PatId], ellipsis: Option<usize> },
+ Record { args: &'a [RecordFieldPat] },
+ Unit,
+}
+
+/// We need to do pattern matching in two phases: One to check if the pattern matches, and one to fill the bindings
+/// of patterns. This is necessary to prevent double moves and similar problems. For example:
+/// ```ignore
+/// struct X;
+/// match (X, 3) {
+/// (b, 2) | (b, 3) => {},
+/// _ => {}
+/// }
+/// ```
+/// If we do everything in one pass, we will move `X` to the first `b`, then we see that the second field of tuple
+/// doesn't match and we should move the `X` to the second `b` (which here is the same thing, but doesn't need to be) and
+/// it might even doesn't match the second pattern and we may want to not move `X` at all.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum MatchingMode {
+ /// Check that if this pattern matches
+ Check,
+ /// Assume that this pattern matches, fill bindings
+ Bind,
+}
+
+impl MirLowerCtx<'_> {
+ /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
+ /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
+ /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
+ /// mismatched path block is `None`.
+ ///
+ /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
+ /// `current_else` argument to save an unnecessary jump. If `current_else` isn't `None`, the result mismatched path
+ /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
+ /// so it should be an empty block.
+ pub(super) fn pattern_match(
+ &mut self,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ cond_place: Place,
+ pattern: PatId,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place.clone(),
+ pattern,
+ MatchingMode::Check,
+ )?;
+ let (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place,
+ pattern,
+ MatchingMode::Bind,
+ )?;
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_inner(
+ &mut self,
+ mut current: BasicBlockId,
+ mut current_else: Option<BasicBlockId>,
+ mut cond_place: Place,
+ pattern: PatId,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
+ cond_place.projection = cond_place
+ .projection
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into();
+ Ok(match &self.body.pats[pattern] {
+ Pat::Missing => return Err(MirLowerError::IncompletePattern),
+ Pat::Wild => (current, current_else),
+ Pat::Tuple { args, ellipsis } => {
+ let subst = match self.infer[pattern].kind(Interner) {
+ TyKind::Tuple(_, s) => s,
+ _ => {
+ return Err(MirLowerError::TypeError(
+ "non tuple type matched with tuple pattern",
+ ))
+ }
+ };
+ self.pattern_match_tuple_like(
+ current,
+ current_else,
+ args,
+ *ellipsis,
+ (0..subst.len(Interner)).map(|i| PlaceElem::TupleOrClosureField(i)),
+ &(&mut cond_place),
+ mode,
+ )?
+ }
+ Pat::Or(pats) => {
+ let then_target = self.new_basic_block();
+ let mut finished = false;
+ for pat in &**pats {
+ let (mut next, next_else) = self.pattern_match_inner(
+ current,
+ None,
+ (&mut cond_place).clone(),
+ *pat,
+ MatchingMode::Check,
+ )?;
+ if mode == MatchingMode::Bind {
+ (next, _) = self.pattern_match_inner(
+ next,
+ None,
+ (&mut cond_place).clone(),
+ *pat,
+ MatchingMode::Bind,
+ )?;
+ }
+ self.set_goto(next, then_target, pattern.into());
+ match next_else {
+ Some(t) => {
+ current = t;
+ }
+ None => {
+ finished = true;
+ break;
+ }
+ }
+ }
+ if !finished {
+ if mode == MatchingMode::Bind {
+ self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
+ } else {
+ let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
+ self.set_goto(current, ce, pattern.into());
+ }
+ }
+ (then_target, current_else)
+ }
+ Pat::Record { args, .. } => {
+ let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+ not_supported!("unresolved variant for record");
+ };
+ self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Record { args: &*args },
+ mode,
+ )?
+ }
+ Pat::Range { start, end } => {
+ let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> {
+ let lv =
+ self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?;
+ let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
+ let next = self.new_basic_block();
+ let discr: Place =
+ self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(
+ binop,
+ lv,
+ Operand::Copy((&mut cond_place).clone()),
+ ),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ pattern.into(),
+ );
+ current = next;
+ Ok(())
+ };
+ if mode == MatchingMode::Check {
+ if let Some(start) = start {
+ add_check(start, BinOp::Le)?;
+ }
+ if let Some(end) = end {
+ add_check(end, BinOp::Ge)?;
+ }
+ }
+ (current, current_else)
+ }
+ Pat::Slice { prefix, slice, suffix } => {
+ if mode == MatchingMode::Check {
+ // emit runtime length check for slice
+ if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) {
+ let pattern_len = prefix.len() + suffix.len();
+ let place_len: Place =
+ self.temp(TyBuilder::usize(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ place_len.clone(),
+ Rvalue::Len((&mut cond_place).clone()),
+ pattern.into(),
+ );
+ let else_target =
+ *current_else.get_or_insert_with(|| self.new_basic_block());
+ let next = self.new_basic_block();
+ if slice.is_none() {
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(place_len),
+ targets: SwitchTargets::static_if(
+ pattern_len as u128,
+ next,
+ else_target,
+ ),
+ },
+ pattern.into(),
+ );
+ } else {
+ let c = Operand::from_concrete_const(
+ pattern_len.to_le_bytes().to_vec(),
+ MemoryMap::default(),
+ TyBuilder::usize(),
+ );
+ let discr: Place =
+ self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ pattern.into(),
+ );
+ }
+ current = next;
+ }
+ }
+ for (i, &pat) in prefix.iter().enumerate() {
+ let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
+ offset: i as u64,
+ from_end: false,
+ });
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
+ }
+ if let Some(slice) = slice {
+ if mode == MatchingMode::Bind {
+ if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
+ let next_place = (&mut cond_place).project(ProjectionElem::Subslice {
+ from: prefix.len() as u64,
+ to: suffix.len() as u64,
+ });
+ (current, current_else) = self.pattern_match_binding(
+ id,
+ next_place,
+ (*slice).into(),
+ current,
+ current_else,
+ )?;
+ }
+ }
+ }
+ for (i, &pat) in suffix.iter().enumerate() {
+ let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
+ offset: i as u64,
+ from_end: true,
+ });
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
+ }
+ (current, current_else)
+ }
+ Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
+ Some(variant) => self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Unit,
+ mode,
+ )?,
+ None => {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = self.owner.resolver(self.db.upcast());
+ let pr = resolver
+ .resolve_path_in_value_ns(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?;
+ let (c, subst) = 'b: {
+ if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
+ if let AssocItemId::ConstId(c) = x.0 {
+ break 'b (c, x.1);
+ }
+ }
+ if let ResolveValueResult::ValueNs(v) = pr {
+ if let ValueNs::ConstId(c) = v {
+ break 'b (c, Substitution::empty(Interner));
+ }
+ }
+ not_supported!("path in pattern position that is not const or variant")
+ };
+ let tmp: Place =
+ self.temp(self.infer[pattern].clone(), current, pattern.into())?.into();
+ let span = pattern.into();
+ self.lower_const(
+ c.into(),
+ current,
+ tmp.clone(),
+ subst,
+ span,
+ self.infer[pattern].clone(),
+ )?;
+ let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ tmp2.clone(),
+ Rvalue::CheckedBinaryOp(
+ BinOp::Eq,
+ Operand::Copy(tmp),
+ Operand::Copy(cond_place),
+ ),
+ span,
+ );
+ let next = self.new_basic_block();
+ let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(tmp2),
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ span,
+ );
+ (next, Some(else_target))
+ }
+ },
+ Pat::Lit(l) => match &self.body.exprs[*l] {
+ Expr::Literal(l) => {
+ let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
+ if mode == MatchingMode::Check {
+ self.pattern_match_const(current_else, current, c, cond_place, pattern)?
+ } else {
+ (current, current_else)
+ }
+ }
+ _ => not_supported!("expression path literal"),
+ },
+ Pat::Bind { id, subpat } => {
+ if let Some(subpat) = subpat {
+ (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ (&mut cond_place).clone(),
+ *subpat,
+ mode,
+ )?
+ }
+ if mode == MatchingMode::Bind {
+ self.pattern_match_binding(
+ *id,
+ cond_place,
+ pattern.into(),
+ current,
+ current_else,
+ )?
+ } else {
+ (current, current_else)
+ }
+ }
+ Pat::TupleStruct { path: _, args, ellipsis } => {
+ let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+ not_supported!("unresolved variant");
+ };
+ self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Tuple { args, ellipsis: *ellipsis },
+ mode,
+ )?
+ }
+ Pat::Ref { pat, mutability: _ } => self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place.project(ProjectionElem::Deref),
+ *pat,
+ mode,
+ )?,
+ Pat::Box { .. } => not_supported!("box pattern"),
+ Pat::ConstBlock(_) => not_supported!("const block pattern"),
+ })
+ }
+
+ fn pattern_match_binding(
+ &mut self,
+ id: BindingId,
+ cond_place: Place,
+ span: MirSpan,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let target_place = self.binding_local(id)?;
+ let mode = self.infer.binding_modes[id];
+ self.push_storage_live(id, current)?;
+ self.push_assignment(
+ current,
+ target_place.into(),
+ match mode {
+ BindingMode::Move => Operand::Copy(cond_place).into(),
+ BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place),
+ BindingMode::Ref(Mutability::Mut) => {
+ Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, cond_place)
+ }
+ },
+ span,
+ );
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_const(
+ &mut self,
+ current_else: Option<BasicBlockId>,
+ current: BasicBlockId,
+ c: Operand,
+ cond_place: Place,
+ pattern: Idx<Pat>,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let then_target = self.new_basic_block();
+ let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+ let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, then_target, else_target),
+ },
+ pattern.into(),
+ );
+ Ok((then_target, Some(else_target)))
+ }
+
+ fn pattern_matching_variant(
+ &mut self,
+ cond_place: Place,
+ variant: VariantId,
+ mut current: BasicBlockId,
+ span: MirSpan,
+ mut current_else: Option<BasicBlockId>,
+ shape: AdtPatternShape<'_>,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ Ok(match variant {
+ VariantId::EnumVariantId(v) => {
+ if mode == MatchingMode::Check {
+ let e = self.const_eval_discriminant(v)? as u128;
+ let tmp = self.discr_temp_place(current);
+ self.push_assignment(
+ current,
+ tmp.clone(),
+ Rvalue::Discriminant(cond_place.clone()),
+ span,
+ );
+ let next = self.new_basic_block();
+ let else_target = current_else.get_or_insert_with(|| self.new_basic_block());
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(tmp),
+ targets: SwitchTargets::static_if(e, next, *else_target),
+ },
+ span,
+ );
+ current = next;
+ }
+ let enum_data = self.db.enum_data(v.parent);
+ self.pattern_matching_variant_fields(
+ shape,
+ &enum_data.variants[v.local_id].variant_data,
+ variant,
+ current,
+ current_else,
+ &cond_place,
+ mode,
+ )?
+ }
+ VariantId::StructId(s) => {
+ let struct_data = self.db.struct_data(s);
+ self.pattern_matching_variant_fields(
+ shape,
+ &struct_data.variant_data,
+ variant,
+ current,
+ current_else,
+ &cond_place,
+ mode,
+ )?
+ }
+ VariantId::UnionId(_) => {
+ return Err(MirLowerError::TypeError("pattern matching on union"))
+ }
+ })
+ }
+
+ fn pattern_matching_variant_fields(
+ &mut self,
+ shape: AdtPatternShape<'_>,
+ variant_data: &VariantData,
+ v: VariantId,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ Ok(match shape {
+ AdtPatternShape::Record { args } => {
+ let it = args
+ .iter()
+ .map(|x| {
+ let field_id =
+ variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?;
+ Ok((
+ PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }),
+ x.pat,
+ ))
+ })
+ .collect::<Result<Vec<_>>>()?;
+ self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)?
+ }
+ AdtPatternShape::Tuple { args, ellipsis } => {
+ let fields = variant_data
+ .fields()
+ .iter()
+ .map(|(x, _)| PlaceElem::Field(FieldId { parent: v.into(), local_id: x }));
+ self.pattern_match_tuple_like(
+ current,
+ current_else,
+ args,
+ ellipsis,
+ fields,
+ cond_place,
+ mode,
+ )?
+ }
+ AdtPatternShape::Unit => (current, current_else),
+ })
+ }
+
+ fn pattern_match_adt(
+ &mut self,
+ mut current: BasicBlockId,
+ mut current_else: Option<BasicBlockId>,
+ args: impl Iterator<Item = (PlaceElem, PatId)>,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ for (proj, arg) in args {
+ let cond_place = cond_place.project(proj);
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
+ }
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_tuple_like(
+ &mut self,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ args: &[PatId],
+ ellipsis: Option<usize>,
+ fields: impl DoubleEndedIterator<Item = PlaceElem> + Clone,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let it = al
+ .iter()
+ .zip(fields.clone())
+ .chain(ar.iter().rev().zip(fields.rev()))
+ .map(|(x, y)| (y, *x));
+ self.pattern_match_adt(current, current_else, it, cond_place, mode)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
new file mode 100644
index 000000000..ce3f7a8e5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -0,0 +1,351 @@
+//! Monomorphization of mir, which is used in mir interpreter and const eval.
+//!
+//! The job of monomorphization is:
+//! * Monomorphization. That is, replacing `Option<T>` with `Option<i32>` where `T:=i32` substitution
+//! is provided
+//! * Normalizing types, for example replacing RPIT of other functions called in this body.
+//!
+//! So the monomorphization should be called even if the substitution is empty.
+
+use std::mem;
+
+use chalk_ir::{
+ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+ ConstData, DebruijnIndex,
+};
+use hir_def::{DefWithBodyId, GeneralConstId};
+use triomphe::Arc;
+
+use crate::{
+ consteval::unknown_const,
+ db::HirDatabase,
+ from_placeholder_idx,
+ infer::normalize,
+ method_resolution::lookup_impl_const,
+ utils::{generics, Generics},
+ ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
+};
+
+use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+struct Filler<'a> {
+ db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
+ subst: &'a Substitution,
+ generics: Option<Generics>,
+ owner: DefWithBodyId,
+}
+impl FallibleTypeFolder<Interner> for Filler<'_> {
+ type Error = MirLowerError;
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_ty(
+ &mut self,
+ ty: Ty,
+ outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ match ty.kind(Interner) {
+ TyKind::AssociatedType(id, subst) => {
+ // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
+ // this kind of associated types.
+ Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
+ associated_ty_id: *id,
+ substitution: subst.clone().try_fold_with(self, outer_binder)?,
+ }))
+ .intern(Interner))
+ }
+ TyKind::OpaqueType(id, subst) => {
+ let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+ let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
+ match impl_trait_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ let infer = self.db.infer(func.into());
+ let filler = &mut Filler {
+ db: self.db,
+ owner: self.owner,
+ trait_env: self.trait_env.clone(),
+ subst: &subst,
+ generics: Some(generics(self.db.upcast(), func.into())),
+ };
+ filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+ }
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+ not_supported!("async block impl trait");
+ }
+ }
+ }
+ _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+ }
+ }
+
+ fn try_fold_free_placeholder_const(
+ &mut self,
+ _ty: chalk_ir::Ty<Interner>,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.constant(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_free_placeholder_ty(
+ &mut self,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.ty(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_const(
+ &mut self,
+ constant: chalk_ir::Const<Interner>,
+ outer_binder: DebruijnIndex,
+ ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+ let next_ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?,
+ );
+ ConstData { ty: next_ty, value: constant.data(Interner).value.clone() }
+ .intern(Interner)
+ .try_super_fold_with(self, outer_binder)
+ }
+}
+
+impl Filler<'_> {
+ fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, TyKind::Error.intern(Interner));
+ *ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?,
+ );
+ Ok(())
+ }
+
+ fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone()));
+ *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, Substitution::empty(Interner));
+ *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
+ match op {
+ Operand::Constant(c) => {
+ match &c.data(Interner).value {
+ chalk_ir::ConstValue::BoundVar(b) => {
+ let resolved = self
+ .subst
+ .as_slice(Interner)
+ .get(b.index)
+ .ok_or_else(|| {
+ MirLowerError::GenericArgNotProvided(
+ self.generics
+ .as_ref()
+ .and_then(|x| x.iter().nth(b.index))
+ .unwrap()
+ .0,
+ self.subst.clone(),
+ )
+ })?
+ .assert_const_ref(Interner);
+ *c = resolved.clone();
+ }
+ chalk_ir::ConstValue::InferenceVar(_)
+ | chalk_ir::ConstValue::Placeholder(_) => {}
+ chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
+ let mut const_id = *const_id;
+ let mut subst = subst.clone();
+ self.fill_subst(&mut subst)?;
+ if let GeneralConstId::ConstId(c) = const_id {
+ let (c, s) = lookup_impl_const(
+ self.db,
+ self.db.trait_environment_for_body(self.owner),
+ c,
+ subst,
+ );
+ const_id = GeneralConstId::ConstId(c);
+ subst = s;
+ }
+ let result =
+ self.db.const_eval(const_id.into(), subst).map_err(|e| {
+ let name = const_id.name(self.db.upcast());
+ MirLowerError::ConstEvalError(name, Box::new(e))
+ })?;
+ *c = result;
+ }
+ crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
+ },
+ }
+ self.fill_const(c)?;
+ }
+ Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (),
+ }
+ Ok(())
+ }
+
+ fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> {
+ for (_, l) in body.locals.iter_mut() {
+ self.fill_ty(&mut l.ty)?;
+ }
+ for (_, bb) in body.basic_blocks.iter_mut() {
+ for statement in &mut bb.statements {
+ match &mut statement.kind {
+ StatementKind::Assign(_, r) => match r {
+ Rvalue::Aggregate(ak, ops) => {
+ for op in &mut **ops {
+ self.fill_operand(op)?;
+ }
+ match ak {
+ super::AggregateKind::Array(ty)
+ | super::AggregateKind::Tuple(ty)
+ | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?,
+ super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?,
+ super::AggregateKind::Union(_, _) => (),
+ }
+ }
+ Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => {
+ self.fill_ty(ty)?;
+ }
+ Rvalue::Use(op) => {
+ self.fill_operand(op)?;
+ }
+ Rvalue::Repeat(op, len) => {
+ self.fill_operand(op)?;
+ self.fill_const(len)?;
+ }
+ Rvalue::Ref(_, _)
+ | Rvalue::Len(_)
+ | Rvalue::Cast(_, _, _)
+ | Rvalue::CheckedBinaryOp(_, _, _)
+ | Rvalue::UnaryOp(_, _)
+ | Rvalue::Discriminant(_)
+ | Rvalue::CopyForDeref(_) => (),
+ },
+ StatementKind::Deinit(_)
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ if let Some(terminator) = &mut bb.terminator {
+ match &mut terminator.kind {
+ TerminatorKind::Call { func, args, .. } => {
+ self.fill_operand(func)?;
+ for op in &mut **args {
+ self.fill_operand(op)?;
+ }
+ }
+ TerminatorKind::SwitchInt { discr, .. } => {
+ self.fill_operand(discr)?;
+ }
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => (),
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+pub fn monomorphized_mir_body_query(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body(owner)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+pub fn monomorphized_mir_body_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &DefWithBodyId,
+ _: &Substitution,
+ _: &Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ return Err(MirLowerError::Loop);
+}
+
+pub fn monomorphized_mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let (owner, _) = db.lookup_intern_closure(closure.into());
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body_for_closure(closure)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query.
+pub fn monomorphize_mir_body_bad(
+ db: &dyn HirDatabase,
+ mut body: MirBody,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<MirBody, MirLowerError> {
+ let owner = body.owner;
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ filler.fill_body(&mut body)?;
+ Ok(body)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index ffc08b7e3..ac23e77bd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -1,28 +1,83 @@
//! A pretty-printer for MIR.
-use std::fmt::{Display, Write};
+use std::{
+ fmt::{Debug, Display, Write},
+ mem,
+};
-use hir_def::{body::Body, expr::BindingId};
+use hir_def::{body::Body, hir::BindingId};
use hir_expand::name::Name;
use la_arena::ArenaMap;
use crate::{
db::HirDatabase,
- display::HirDisplay,
- mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
+ display::{ClosureStyle, HirDisplay},
+ mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
+ ClosureId,
};
use super::{
AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
};
+macro_rules! w {
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = write!($dst, $($arg)*); }
+ };
+}
+
+macro_rules! wln {
+ ($dst:expr) => {
+ { let _ = writeln!($dst); }
+ };
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = writeln!($dst, $($arg)*); }
+ };
+}
+
impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
let hir_body = db.body(self.owner);
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
- ctx.for_body();
+ ctx.for_body(|this| match ctx.body.owner {
+ hir_def::DefWithBodyId::FunctionId(id) => {
+ let data = db.function_data(id);
+ w!(this, "fn {}() ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::StaticId(id) => {
+ let data = db.static_data(id);
+ w!(this, "static {}: _ = ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::ConstId(id) => {
+ let data = db.const_data(id);
+ w!(
+ this,
+ "const {}: _ = ",
+ data.name.as_ref().unwrap_or(&Name::missing()).display(db.upcast())
+ );
+ }
+ hir_def::DefWithBodyId::VariantId(id) => {
+ let data = db.enum_data(id.parent);
+ w!(this, "enum {} = ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::InTypeConstId(id) => {
+ w!(this, "in type const {id:?} = ");
+ }
+ });
ctx.result
}
+
+ // String with lines is rendered poorly in `dbg` macros, which I use very much, so this
+ // function exists to solve that.
+ pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
+ struct StringDbg(String);
+ impl Debug for StringDbg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.0)
+ }
+ }
+ StringDbg(self.pretty_print(db))
+ }
}
struct MirPrettyCtx<'a> {
@@ -30,25 +85,10 @@ struct MirPrettyCtx<'a> {
hir_body: &'a Body,
db: &'a dyn HirDatabase,
result: String,
- ident: String,
+ indent: String,
local_to_binding: ArenaMap<LocalId, BindingId>,
}
-macro_rules! w {
- ($dst:expr, $($arg:tt)*) => {
- { let _ = write!($dst, $($arg)*); }
- };
-}
-
-macro_rules! wln {
- ($dst:expr) => {
- { let _ = writeln!($dst); }
- };
- ($dst:expr, $($arg:tt)*) => {
- { let _ = writeln!($dst, $($arg)*); }
- };
-}
-
impl Write for MirPrettyCtx<'_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
let mut it = s.split('\n'); // note: `.lines()` is wrong here
@@ -66,31 +106,62 @@ enum LocalName {
Binding(Name, LocalId),
}
-impl Display for LocalName {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl HirDisplay for LocalName {
+ fn hir_fmt(
+ &self,
+ f: &mut crate::display::HirFormatter<'_>,
+ ) -> Result<(), crate::display::HirDisplayError> {
match self {
LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
- LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())),
+ LocalName::Binding(n, l) => {
+ write!(f, "{}_{}", n.display(f.db.upcast()), u32::from(l.into_raw()))
+ }
}
}
}
impl<'a> MirPrettyCtx<'a> {
- fn for_body(&mut self) {
+ fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
+ name(self);
self.with_block(|this| {
this.locals();
wln!(this);
this.blocks();
});
+ for &closure in &self.body.closures {
+ self.for_closure(closure);
+ }
+ }
+
+ fn for_closure(&mut self, closure: ClosureId) {
+ let body = match self.db.mir_body_for_closure(closure) {
+ Ok(x) => x,
+ Err(e) => {
+ wln!(self, "// error in {closure:?}: {e:?}");
+ return;
+ }
+ };
+ let result = mem::take(&mut self.result);
+ let indent = mem::take(&mut self.indent);
+ let mut ctx = MirPrettyCtx {
+ body: &body,
+ local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
+ result,
+ indent,
+ ..*self
+ };
+ ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
+ self.result = ctx.result;
+ self.indent = ctx.indent;
}
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
- self.ident += " ";
+ self.indent += " ";
wln!(self, "{{");
f(self);
for _ in 0..4 {
self.result.pop();
- self.ident.pop();
+ self.indent.pop();
}
wln!(self, "}}");
}
@@ -101,7 +172,7 @@ impl<'a> MirPrettyCtx<'a> {
body,
db,
result: String::new(),
- ident: String::new(),
+ indent: String::new(),
local_to_binding,
hir_body,
}
@@ -109,7 +180,7 @@ impl<'a> MirPrettyCtx<'a> {
fn write_line(&mut self) {
self.result.push('\n');
- self.result += &self.ident;
+ self.result += &self.indent;
}
fn write(&mut self, line: &str) {
@@ -118,7 +189,12 @@ impl<'a> MirPrettyCtx<'a> {
fn locals(&mut self) {
for (id, local) in self.body.locals.iter() {
- wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db));
+ wln!(
+ self,
+ "let {}: {};",
+ self.local_name(id).display(self.db),
+ self.hir_display(&local.ty)
+ );
}
}
@@ -147,10 +223,10 @@ impl<'a> MirPrettyCtx<'a> {
wln!(this, ";");
}
StatementKind::StorageDead(p) => {
- wln!(this, "StorageDead({})", this.local_name(*p));
+ wln!(this, "StorageDead({})", this.local_name(*p).display(self.db));
}
StatementKind::StorageLive(p) => {
- wln!(this, "StorageLive({})", this.local_name(*p));
+ wln!(this, "StorageLive({})", this.local_name(*p).display(self.db));
}
StatementKind::Deinit(p) => {
w!(this, "Deinit(");
@@ -161,11 +237,11 @@ impl<'a> MirPrettyCtx<'a> {
}
}
match &block.terminator {
- Some(terminator) => match terminator {
- Terminator::Goto { target } => {
+ Some(terminator) => match &terminator.kind {
+ TerminatorKind::Goto { target } => {
wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
}
- Terminator::SwitchInt { discr, targets } => {
+ TerminatorKind::SwitchInt { discr, targets } => {
w!(this, "switch ");
this.operand(discr);
w!(this, " ");
@@ -176,7 +252,7 @@ impl<'a> MirPrettyCtx<'a> {
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
});
}
- Terminator::Call { func, args, destination, target, .. } => {
+ TerminatorKind::Call { func, args, destination, target, .. } => {
w!(this, "Call ");
this.with_block(|this| {
w!(this, "func: ");
@@ -208,7 +284,7 @@ impl<'a> MirPrettyCtx<'a> {
fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
let Some((last, head)) = projections.split_last() else {
// no projection
- w!(this, "{}", this.local_name(local));
+ w!(this, "{}", this.local_name(local).display(this.db));
return;
};
match last {
@@ -226,21 +302,26 @@ impl<'a> MirPrettyCtx<'a> {
f(this, local, head);
let variant_name =
&this.db.enum_data(e.parent).variants[e.local_id].name;
- w!(this, " as {}).{}", variant_name, name);
+ w!(
+ this,
+ " as {}).{}",
+ variant_name.display(this.db.upcast()),
+ name.display(this.db.upcast())
+ );
}
hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
f(this, local, head);
- w!(this, ".{name}");
+ w!(this, ".{}", name.display(this.db.upcast()));
}
}
}
- ProjectionElem::TupleField(x) => {
+ ProjectionElem::TupleOrClosureField(x) => {
f(this, local, head);
w!(this, ".{}", x);
}
ProjectionElem::Index(l) => {
f(this, local, head);
- w!(this, "[{}]", this.local_name(*l));
+ w!(this, "[{}]", this.local_name(*l).display(this.db));
}
x => {
f(this, local, head);
@@ -258,7 +339,8 @@ impl<'a> MirPrettyCtx<'a> {
// equally. Feel free to change it.
self.place(p);
}
- Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)),
+ Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
+ Operand::Static(s) => w!(self, "Static({:?})", s),
}
}
@@ -284,11 +366,21 @@ impl<'a> MirPrettyCtx<'a> {
self.operand_list(x);
w!(self, "]");
}
+ Rvalue::Repeat(op, len) => {
+ w!(self, "[");
+ self.operand(op);
+ w!(self, "; {}]", len.display(self.db));
+ }
Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => {
w!(self, "Adt(");
self.operand_list(x);
w!(self, ")");
}
+ Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
+ w!(self, "Closure(");
+ self.operand_list(x);
+ w!(self, ")");
+ }
Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
w!(self, "Union(");
self.operand_list(x);
@@ -300,9 +392,9 @@ impl<'a> MirPrettyCtx<'a> {
w!(self, ")");
}
Rvalue::Cast(ck, op, ty) => {
- w!(self, "Discriminant({ck:?}");
+ w!(self, "Cast({ck:?}, ");
self.operand(op);
- w!(self, "{})", ty.display(self.db));
+ w!(self, ", {})", self.hir_display(ty));
}
Rvalue::CheckedBinaryOp(b, o1, o2) => {
self.operand(o1);
@@ -322,6 +414,7 @@ impl<'a> MirPrettyCtx<'a> {
self.place(p);
w!(self, ")");
}
+ Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
Rvalue::ShallowInitBox(op, _) => {
w!(self, "ShallowInitBox(");
self.operand(op);
@@ -345,4 +438,8 @@ impl<'a> MirPrettyCtx<'a> {
}
}
}
+
+ fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
+ ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index 8c48331b9..7d19e0a19 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -1,18 +1,18 @@
//! Database used for testing `hir`.
-use std::{
- fmt, panic,
- sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
use base_db::{
- salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
+ salsa::{self, Durability},
+ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
};
use hir_def::{db::DefDatabase, ModuleId};
use hir_expand::db::ExpandDatabase;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use nohash_hasher::IntMap;
+use rustc_hash::FxHashSet;
use syntax::TextRange;
use test_utils::extract_annotations;
+use triomphe::Arc;
#[salsa::database(
base_db::SourceDatabaseExtStorage,
@@ -30,7 +30,7 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
- this.set_enable_proc_attr_macros(true);
+ this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
this
}
}
@@ -74,13 +74,13 @@ impl salsa::ParallelDatabase for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -102,7 +102,7 @@ impl TestDB {
self.module_for_file_opt(file_id).unwrap()
}
- pub(crate) fn extract_annotations(&self) -> NoHashHashMap<FileId, Vec<(TextRange, String)>> {
+ pub(crate) fn extract_annotations(&self) -> IntMap<FileId, Vec<(TextRange, String)>> {
let mut files = Vec::new();
let crate_graph = self.crate_graph();
for krate in crate_graph.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 83d31f002..857141280 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -10,14 +10,14 @@ mod display_source_code;
mod incremental;
mod diagnostics;
-use std::{collections::HashMap, env, sync::Arc};
+use std::{collections::HashMap, env};
use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
db::{DefDatabase, InternDatabase},
- expr::{ExprId, PatId},
+ hir::{ExprId, Pat, PatId},
item_scope::ItemScope,
nameres::DefMap,
src::HasSource,
@@ -32,6 +32,7 @@ use syntax::{
};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
use tracing_tree::HierarchicalLayer;
+use triomphe::Arc;
use crate::{
db::HirDatabase,
@@ -145,13 +146,17 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
- let (_body, body_source_map) = db.body_with_source_map(def);
+ let (body, body_source_map) = db.body_with_source_map(def);
let inference_result = db.infer(def);
- for (pat, ty) in inference_result.type_of_pat.iter() {
+ for (pat, mut ty) in inference_result.type_of_pat.iter() {
+ if let Pat::Bind { id, .. } = body.pats[pat] {
+ ty = &inference_result.type_of_binding[id];
+ }
let node = match pat_node(&body_source_map, pat, &db) {
Some(value) => value,
None => continue,
@@ -159,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
@@ -175,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
@@ -198,8 +203,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
let Some(node) = (match expr_or_pat {
- hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
- hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
+ hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
+ hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
}) else { continue; };
let range = node.as_ref().original_file_range(&db);
let actual = format!(
@@ -246,7 +251,7 @@ fn expr_node(
) -> Option<InFile<SyntaxNode>> {
Some(match body_source_map.expr_syntax(expr) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| ptr.to_node(&root).syntax().clone())
}
Err(SyntheticSyntax) => return None,
@@ -260,7 +265,7 @@ fn pat_node(
) -> Option<InFile<SyntaxNode>> {
Some(match body_source_map.pat_syntax(pat) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| {
ptr.either(
|it| it.to_node(&root).syntax().clone(),
@@ -283,14 +288,18 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let mut buf = String::new();
let mut infer_def = |inference_result: Arc<InferenceResult>,
+ body: Arc<Body>,
body_source_map: Arc<BodySourceMap>| {
let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
- for (pat, ty) in inference_result.type_of_pat.iter() {
+ for (pat, mut ty) in inference_result.type_of_pat.iter() {
+ if let Pat::Bind { id, .. } = body.pats[pat] {
+ ty = &inference_result.type_of_binding[id];
+ }
let syntax_ptr = match body_source_map.pat_syntax(pat) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| {
ptr.either(
|it| it.to_node(&root).syntax().clone(),
@@ -309,7 +318,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
for (expr, ty) in inference_result.type_of_expr.iter() {
let node = match body_source_map.expr_syntax(expr) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| ptr.to_node(&root).syntax().clone())
}
Err(SyntheticSyntax) => continue,
@@ -383,11 +392,12 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
});
for def in defs {
- let (_body, source_map) = db.body_with_source_map(def);
+ let (body, source_map) = db.body_with_source_map(def);
let infer = db.infer(def);
- infer_def(infer, source_map);
+ infer_def(infer, body, source_map);
}
buf.truncate(buf.trim_end().len());
@@ -572,10 +582,9 @@ fn salsa_bug() {
let x = 1;
x.push(1);
}
- "
- .to_string();
+ ";
- db.set_file_text(pos.file_id, Arc::new(new_text));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
let module = db.module_for_file(pos.file_id);
let crate_def_map = module.def_map(&db);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index b524922b6..16e5ef85d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -258,7 +258,6 @@ fn test() {
#[test]
fn coerce_autoderef_block() {
- // FIXME: We should know mutability in overloaded deref
check_no_mismatches(
r#"
//- minicore: deref
@@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
takes_ref_str(&{ returns_string() });
- // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
+ // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
}
"#,
);
@@ -397,9 +396,39 @@ fn test() {
}
#[test]
+fn coerce_fn_item_to_fn_ptr_in_array() {
+ check_no_mismatches(
+ r"
+fn foo(x: u32) -> isize { 1 }
+fn bar(x: u32) -> isize { 1 }
+fn test() {
+ let f = [foo, bar];
+ // ^^^ adjustments: Pointer(ReifyFnPointer)
+}",
+ );
+}
+
+#[test]
fn coerce_fn_items_in_match_arms() {
cov_mark::check!(coerce_fn_reification);
+ check_no_mismatches(
+ r"
+fn foo1(x: u32) -> isize { 1 }
+fn foo2(x: u32) -> isize { 2 }
+fn foo3(x: u32) -> isize { 3 }
+fn test() {
+ let x = match 1 {
+ 1 => foo1,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ 2 => foo2,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ _ => foo3,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ };
+ x;
+}",
+ );
check_types(
r"
fn foo1(x: u32) -> isize { 1 }
@@ -507,7 +536,6 @@ fn test() {
#[test]
fn coerce_unsize_generic() {
- // FIXME: fix the type mismatches here
check(
r#"
//- minicore: coerce_unsized
@@ -516,9 +544,9 @@ struct Bar<T>(Foo<T>);
fn test() {
let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
- //^^^^^^^^^ expected [usize], got [usize; 3]
+ //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]>
let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
- //^^^^^^^^^ expected [usize], got [usize; 3]
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]>
}
"#,
);
@@ -547,7 +575,7 @@ fn two_closures_lub() {
fn foo(c: i32) {
let add = |a: i32, b: i32| a + b;
let sub = |a, b| a - b;
- //^^^^^^^^^^^^ |i32, i32| -> i32
+ //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
if c > 42 { add } else { sub };
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
}
@@ -842,3 +870,74 @@ fn test() {
}",
);
}
+
+#[test]
+fn adjust_index() {
+ check_no_mismatches(
+ r"
+//- minicore: index, slice, coerce_unsized
+fn test() {
+ let x = [1, 2, 3];
+ x[2] = 6;
+ // ^ adjustments: Borrow(Ref(Mut))
+}
+ ",
+ );
+ check_no_mismatches(
+ r"
+//- minicore: index
+struct Struct;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+
+ fn index(&self, index: usize) -> &Self::Output { &() }
+}
+struct StructMut;
+
+impl core::ops::Index<usize> for StructMut {
+ type Output = ();
+
+ fn index(&self, index: usize) -> &Self::Output { &() }
+}
+impl core::ops::IndexMut for StructMut {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () }
+}
+fn test() {
+ Struct[0];
+ // ^^^^^^ adjustments: Borrow(Ref(Not))
+ StructMut[0];
+ // ^^^^^^^^^ adjustments: Borrow(Ref(Not))
+ &mut StructMut[0];
+ // ^^^^^^^^^ adjustments: Borrow(Ref(Mut))
+}",
+ );
+}
+
+#[test]
+fn regression_14443_dyn_coercion_block_impls() {
+ check_no_mismatches(
+ r#"
+//- minicore: coerce_unsized
+trait T {}
+
+fn dyn_t(d: &dyn T) {}
+
+fn main() {
+ struct A;
+ impl T for A {}
+
+ let a = A;
+
+ let b = {
+ struct B;
+ impl T for B {}
+
+ B
+ };
+
+ dyn_t(&a);
+ dyn_t(&b);
+}
+"#,
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
index 073d6d9be..bb15ca8c4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
@@ -1,6 +1,5 @@
-use std::sync::Arc;
-
use base_db::{fixture::WithFixture, SourceDatabaseExt};
+use triomphe::Arc;
use crate::{db::HirDatabase, test_db::TestDB};
@@ -33,10 +32,9 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
+
1
}
- "
- .to_string();
+ ";
- db.set_file_text(pos.file_id, Arc::new(new_text));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
{
let events = db.log_executed(|| {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index 8b75ec842..111ac0b61 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -140,6 +140,7 @@ fn infer_path_qualified_macros_expanded() {
fn expr_macro_def_expanded_in_various_places() {
check_infer(
r#"
+ //- minicore: iterator
macro spam() {
1isize
}
@@ -195,10 +196,19 @@ fn expr_macro_def_expanded_in_various_places() {
!0..6 '1isize': isize
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
+ 100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+ 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': !
+ 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+ 100..119 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
100..119 'for _ ...!() {}': ()
- 104..105 '_': {unknown}
+ 100..119 'for _ ...!() {}': ()
+ 100..119 'for _ ...!() {}': ()
+ 104..105 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
117..119 '{}': ()
- 124..134 '|| spam!()': || -> isize
+ 124..134 '|| spam!()': impl Fn() -> isize
140..156 'while ...!() {}': ()
154..156 '{}': ()
161..174 'break spam!()': !
@@ -221,6 +231,7 @@ fn expr_macro_def_expanded_in_various_places() {
fn expr_macro_rules_expanded_in_various_places() {
check_infer(
r#"
+ //- minicore: iterator
macro_rules! spam {
() => (1isize);
}
@@ -276,10 +287,19 @@ fn expr_macro_rules_expanded_in_various_places() {
!0..6 '1isize': isize
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
+ 114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+ 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': !
+ 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+ 114..133 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
+ 114..133 'for _ ...!() {}': ()
114..133 'for _ ...!() {}': ()
- 118..119 '_': {unknown}
+ 114..133 'for _ ...!() {}': ()
+ 118..119 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
131..133 '{}': ()
- 138..148 '|| spam!()': || -> isize
+ 138..148 '|| spam!()': impl Fn() -> isize
154..170 'while ...!() {}': ()
168..170 '{}': ()
175..188 'break spam!()': !
@@ -661,8 +681,9 @@ fn infer_builtin_macros_line() {
"#,
expect![[r#"
!0..1 '0': i32
+ !0..6 '0asu32': u32
63..87 '{ ...!(); }': ()
- 73..74 'x': i32
+ 73..74 'x': u32
"#]],
);
}
@@ -699,8 +720,9 @@ fn infer_builtin_macros_column() {
"#,
expect![[r#"
!0..1 '0': i32
+ !0..6 '0asu32': u32
65..91 '{ ...!(); }': ()
- 75..76 'x': i32
+ 75..76 'x': u32
"#]],
);
}
@@ -945,7 +967,7 @@ fn infer_builtin_macros_concat_with_lazy() {
#[test]
fn infer_builtin_macros_env() {
- check_infer(
+ check_types(
r#"
//- /main.rs env:foo=bar
#[rustc_builtin_macro]
@@ -953,13 +975,26 @@ fn infer_builtin_macros_env() {
fn main() {
let x = env!("foo");
+ //^ &str
+ }
+ "#,
+ );
+}
+
+#[test]
+fn infer_builtin_macros_option_env() {
+ check_types(
+ r#"
+ //- minicore: option
+ //- /main.rs env:foo=bar
+ #[rustc_builtin_macro]
+ macro_rules! option_env {() => {}}
+
+ fn main() {
+ let x = option_env!("foo");
+ //^ Option<&str>
}
"#,
- expect![[r#"
- !0..22 '"__RA_...TED__"': &str
- 62..90 '{ ...o"); }': ()
- 72..73 'x': &str
- "#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 378d47833..1e57a4ae2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -389,6 +389,24 @@ mod bar_test {
}
#[test]
+fn infer_trait_method_multiple_mutable_reference() {
+ check_types(
+ r#"
+trait Trait {
+ fn method(&mut self) -> i32 { 5 }
+}
+struct S;
+impl Trait for &mut &mut S {}
+fn test() {
+ let s = &mut &mut &mut S;
+ s.method();
+ //^^^^^^^^^^ i32
+}
+ "#,
+ );
+}
+
+#[test]
fn infer_trait_method_generic_1() {
// the trait implementation is intentionally incomplete -- it shouldn't matter
check_types(
@@ -1255,7 +1273,6 @@ fn foo<T: Trait>(a: &T) {
#[test]
fn autoderef_visibility_field() {
- // FIXME: We should know mutability in overloaded deref
check(
r#"
//- minicore: deref
@@ -1277,7 +1294,7 @@ mod a {
mod b {
fn foo() {
let x = super::a::Bar::new().0;
- // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
+ // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
}
}
@@ -1723,7 +1740,7 @@ fn test() {
Foo.foo();
//^^^ adjustments: Borrow(Ref(Not))
(&Foo).foo();
- // ^^^^ adjustments: ,
+ // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
}
"#,
);
@@ -1922,3 +1939,54 @@ fn foo() {
"#,
);
}
+
+#[test]
+fn box_deref_is_builtin() {
+ check(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+
+#[lang = "owned_box"]
+struct Box<T>(*mut T);
+
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ loop {}
+ }
+}
+
+impl<T> Deref for Box<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target;
+}
+
+struct Foo;
+impl Foo {
+ fn foo(&self) {}
+}
+fn test() {
+ Box::new(Foo).foo();
+ //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref(Not))
+}
+"#,
+ );
+}
+
+#[test]
+fn manually_drop_deref_is_not_builtin() {
+ check(
+ r#"
+//- minicore: manually_drop, deref
+struct Foo;
+impl Foo {
+ fn foo(&self) {}
+}
+use core::mem::ManuallyDrop;
+fn test() {
+ ManuallyDrop::new(Foo).foo();
+ //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index fbdc8209f..59046c043 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -327,6 +327,7 @@ fn diverging_expression_2() {
fn diverging_expression_3_break() {
check_infer_with_mismatches(
r"
+ //- minicore: iterator
//- /main.rs
fn test1() {
// should give type mismatch
@@ -360,6 +361,15 @@ fn diverging_expression_3_break() {
97..343 '{ ...; }; }': ()
140..141 'x': u32
149..175 '{ for ...; }; }': u32
+ 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 151..172 'for a ...eak; }': {unknown}
+ 151..172 'for a ...eak; }': !
+ 151..172 'for a ...eak; }': {unknown}
+ 151..172 'for a ...eak; }': &mut {unknown}
+ 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 151..172 'for a ...eak; }': Option<{unknown}>
+ 151..172 'for a ...eak; }': ()
+ 151..172 'for a ...eak; }': ()
151..172 'for a ...eak; }': ()
155..156 'a': {unknown}
160..161 'b': {unknown}
@@ -367,12 +377,30 @@ fn diverging_expression_3_break() {
164..169 'break': !
226..227 'x': u32
235..253 '{ for ... {}; }': u32
+ 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 237..250 'for a in b {}': {unknown}
+ 237..250 'for a in b {}': !
+ 237..250 'for a in b {}': {unknown}
+ 237..250 'for a in b {}': &mut {unknown}
+ 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 237..250 'for a in b {}': Option<{unknown}>
+ 237..250 'for a in b {}': ()
+ 237..250 'for a in b {}': ()
237..250 'for a in b {}': ()
241..242 'a': {unknown}
246..247 'b': {unknown}
248..250 '{}': ()
304..305 'x': u32
313..340 '{ for ...; }; }': u32
+ 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 315..337 'for a ...urn; }': {unknown}
+ 315..337 'for a ...urn; }': !
+ 315..337 'for a ...urn; }': {unknown}
+ 315..337 'for a ...urn; }': &mut {unknown}
+ 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 315..337 'for a ...urn; }': Option<{unknown}>
+ 315..337 'for a ...urn; }': ()
+ 315..337 'for a ...urn; }': ()
315..337 'for a ...urn; }': ()
319..320 'a': {unknown}
324..325 'b': {unknown}
@@ -483,3 +511,22 @@ fn example() -> bool {
"#,
);
}
+
+#[test]
+fn reservation_impl_should_be_ignored() {
+ // See rust-lang/rust#64631.
+ check_types(
+ r#"
+//- minicore: from
+struct S;
+#[rustc_reservation_impl]
+impl<T> From<!> for T {}
+fn foo<T, U: From<T>>(_: U) -> T { loop {} }
+
+fn test() {
+ let s = foo(S);
+ //^ S
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 74bcab6ca..0f5a3e175 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -1,11 +1,12 @@
use expect_test::expect;
-use super::{check, check_infer, check_infer_with_mismatches, check_types};
+use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
#[test]
fn infer_pattern() {
check_infer(
r#"
+ //- minicore: iterator
fn test(x: &i32) {
let y = x;
let &z = x;
@@ -46,6 +47,15 @@ fn infer_pattern() {
82..94 '(1, "hello")': (i32, &str)
83..84 '1': i32
86..93 '"hello"': &str
+ 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 101..151 'for (e... }': {unknown}
+ 101..151 'for (e... }': !
+ 101..151 'for (e... }': {unknown}
+ 101..151 'for (e... }': &mut {unknown}
+ 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 101..151 'for (e... }': Option<({unknown}, {unknown})>
+ 101..151 'for (e... }': ()
+ 101..151 'for (e... }': ()
101..151 'for (e... }': ()
105..111 '(e, f)': ({unknown}, {unknown})
106..107 'e': {unknown}
@@ -70,8 +80,8 @@ fn infer_pattern() {
228..233 '&true': &bool
229..233 'true': bool
234..236 '{}': ()
- 246..252 'lambda': |u64, u64, i32| -> i32
- 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
+ 246..252 'lambda': impl Fn(u64, u64, i32) -> i32
+ 255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
256..257 'a': u64
264..265 'b': u64
267..268 'c': i32
@@ -241,6 +251,21 @@ fn infer_pattern_match_ergonomics_ref() {
}
#[test]
+fn ref_pat_with_inference_variable() {
+ check_no_mismatches(
+ r#"
+enum E { A }
+fn test() {
+ let f = |e| match e {
+ &E::A => {}
+ };
+ f(&E::A);
+}
+"#,
+ );
+}
+
+#[test]
fn infer_pattern_match_slice() {
check_infer(
r#"
@@ -476,7 +501,7 @@ fn infer_adt_pattern() {
183..184 'x': usize
190..191 'x': usize
201..205 'E::B': E
- 209..212 'foo': {unknown}
+ 209..212 'foo': bool
216..217 '1': usize
227..231 'E::B': E
235..237 '10': usize
@@ -677,25 +702,25 @@ fn test() {
51..58 'loop {}': !
56..58 '{}': ()
72..171 '{ ... x); }': ()
- 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
+ 78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32
78..105 'foo(&(...y)| x)': i32
82..91 '&(1, "a")': &(i32, &str)
83..91 '(1, "a")': (i32, &str)
84..85 '1': i32
87..90 '"a"': &str
- 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
+ 93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32
94..101 '&(x, y)': &(i32, &str)
95..101 '(x, y)': (i32, &str)
96..97 'x': i32
99..100 'y': &str
103..104 'x': i32
- 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
+ 142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32
142..168 'foo(&(...y)| x)': &i32
146..155 '&(1, "a")': &(i32, &str)
147..155 '(1, "a")': (i32, &str)
148..149 '1': i32
151..154 '"a"': &str
- 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
+ 157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32
158..164 '(x, y)': (i32, &str)
159..160 'x': &i32
162..163 'y': &&str
@@ -1084,7 +1109,7 @@ fn var_args() {
#[lang = "va_list"]
pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {}
- //^^^ VaListImpl
+ //^^^ VaListImpl<'_>
"#,
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 689f0da44..047900a32 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -246,6 +246,7 @@ fn infer_std_crash_5() {
// taken from rustc
check_infer(
r#"
+ //- minicore: iterator
fn extra_compiler_flags() {
for content in doesnt_matter {
let name = if doesnt_matter {
@@ -264,13 +265,22 @@ fn infer_std_crash_5() {
"#,
expect![[r#"
26..322 '{ ... } }': ()
+ 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 32..320 'for co... }': {unknown}
+ 32..320 'for co... }': !
+ 32..320 'for co... }': {unknown}
+ 32..320 'for co... }': &mut {unknown}
+ 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 32..320 'for co... }': Option<{unknown}>
+ 32..320 'for co... }': ()
+ 32..320 'for co... }': ()
32..320 'for co... }': ()
36..43 'content': {unknown}
47..60 'doesnt_matter': {unknown}
61..320 '{ ... }': ()
75..79 'name': &{unknown}
82..166 'if doe... }': &{unknown}
- 85..98 'doesnt_matter': {unknown}
+ 85..98 'doesnt_matter': bool
99..128 '{ ... }': &{unknown}
113..118 'first': &{unknown}
134..166 '{ ... }': &{unknown}
@@ -279,7 +289,7 @@ fn infer_std_crash_5() {
181..188 'content': &{unknown}
191..313 'if ICE... }': &{unknown}
194..231 'ICE_RE..._VALUE': {unknown}
- 194..247 'ICE_RE...&name)': {unknown}
+ 194..247 'ICE_RE...&name)': bool
241..246 '&name': &&{unknown}
242..246 'name': &{unknown}
248..276 '{ ... }': &{unknown}
@@ -805,19 +815,19 @@ fn issue_4966() {
225..229 'iter': T
244..246 '{}': Vec<A>
258..402 '{ ...r(); }': ()
- 268..273 'inner': Map<|&f64| -> f64>
- 276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
- 285..298 '|_: &f64| 0.0': |&f64| -> f64
+ 268..273 'inner': Map<impl Fn(&f64) -> f64>
+ 276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
+ 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
286..287 '_': &f64
295..298 '0.0': f64
- 311..317 'repeat': Repeat<Map<|&f64| -> f64>>
- 320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
- 338..343 'inner': Map<|&f64| -> f64>
- 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
- 362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
- 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
- 372..378 'repeat': Repeat<Map<|&f64| -> f64>>
- 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
+ 311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+ 320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
+ 338..343 'inner': Map<impl Fn(&f64) -> f64>
+ 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>, Repeat<Map<impl Fn(&f64) -> f64>>>(Repeat<Map<impl Fn(&f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+ 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
386..399 'vec.foo_bar()': {unknown}
"#]],
);
@@ -852,7 +862,7 @@ fn main() {
123..126 'S()': S<i32>
132..133 's': S<i32>
132..144 's.g(|_x| {})': ()
- 136..143 '|_x| {}': |&i32| -> ()
+ 136..143 '|_x| {}': impl Fn(&i32)
137..139 '_x': &i32
141..143 '{}': ()
150..151 's': S<i32>
@@ -886,13 +896,13 @@ fn flush(&self) {
"#,
expect![[r#"
123..127 'self': &Mutex<T>
- 150..152 '{}': MutexGuard<T>
+ 150..152 '{}': MutexGuard<'_, T>
234..238 'self': &{unknown}
240..290 '{ ...()); }': ()
250..251 'w': &Mutex<BufWriter>
276..287 '*(w.lock())': BufWriter
278..279 'w': &Mutex<BufWriter>
- 278..286 'w.lock()': MutexGuard<BufWriter>
+ 278..286 'w.lock()': MutexGuard<'_, BufWriter>
"#]],
);
}
@@ -1060,7 +1070,7 @@ fn infix_parse<T, S>(_state: S, _level_code: &Fn(S)) -> T {
loop {}
}
-fn parse_arule() {
+fn parse_a_rule() {
infix_parse((), &(|_recurse| ()))
}
"#,
@@ -1068,6 +1078,23 @@ fn parse_arule() {
}
#[test]
+fn nested_closure() {
+ check_types(
+ r#"
+//- minicore: fn, option
+
+fn map<T, U>(o: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> { loop {} }
+
+fn test() {
+ let o = Some(Some(2));
+ map(o, |s| map(s, |x| x));
+ // ^ i32
+}
+ "#,
+ );
+}
+
+#[test]
fn call_expected_type_closure() {
check_types(
r#"
@@ -1198,6 +1225,7 @@ fn mamba(a: U32!(), p: u32) -> u32 {
fn for_loop_block_expr_iterable() {
check_infer(
r#"
+//- minicore: iterator
fn test() {
for _ in { let x = 0; } {
let y = 0;
@@ -1206,8 +1234,17 @@ fn test() {
"#,
expect![[r#"
10..68 '{ ... } }': ()
+ 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter
+ 16..66 'for _ ... }': IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': !
+ 16..66 'for _ ... }': IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': fn next<IntoIterator::IntoIter<()>>(&mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item>
+ 16..66 'for _ ... }': Option<Iterator::Item<IntoIterator::IntoIter<()>>>
+ 16..66 'for _ ... }': ()
16..66 'for _ ... }': ()
- 20..21 '_': {unknown}
+ 16..66 'for _ ... }': ()
+ 20..21 '_': Iterator::Item<IntoIterator::IntoIter<()>>
25..39 '{ let x = 0; }': ()
31..32 'x': i32
35..36 '0': i32
@@ -1458,13 +1495,12 @@ fn regression_11688_3() {
struct Ar<T, const N: u8>(T);
fn f<const LEN: usize, T, const BASE: u8>(
num_zeros: usize,
- ) -> dyn Iterator<Item = [Ar<T, BASE>; LEN]> {
+ ) -> &dyn Iterator<Item = [Ar<T, BASE>; LEN]> {
loop {}
}
fn dynamic_programming() {
- for board in f::<9, u8, 7>(1) {
- //^^^^^ [Ar<u8, 7>; 9]
- }
+ let board = f::<9, u8, 7>(1).next();
+ //^^^^^ Option<[Ar<u8, 7>; 9]>
}
"#,
);
@@ -1758,6 +1794,21 @@ const C: usize = 2 + 2;
}
#[test]
+fn regression_14456() {
+ check_types(
+ r#"
+//- minicore: future
+async fn x() {}
+fn f() {
+ let fut = x();
+ let t = [0u8; { let a = 2 + 2; a }];
+ //^ [u8; 4]
+}
+"#,
+ );
+}
+
+#[test]
fn regression_14164() {
check_types(
r#"
@@ -1788,3 +1839,142 @@ where
"#,
);
}
+
+#[test]
+fn match_ergonomics_with_binding_modes_interaction() {
+ check_types(
+ r"
+enum E { A }
+fn foo() {
+ match &E::A {
+ b @ (x @ E::A | x) => {
+ b;
+ //^ &E
+ x;
+ //^ &E
+ }
+ }
+}",
+ );
+}
+
+#[test]
+fn regression_14844() {
+ check_no_mismatches(
+ r#"
+pub type Ty = Unknown;
+
+pub struct Inner<T>();
+
+pub struct Outer {
+ pub inner: Inner<Ty>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<i32>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const ONE: usize = 1;
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<1>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const ONE: usize = unknown();
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<1>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const N: usize = 2 + 2;
+
+fn f(t: [u8; N]) {}
+
+fn main() {
+ let a = [1, 2, 3, 4];
+ f(a);
+ let b = [1; 4];
+ let c: [u8; N] = b;
+ let d = [1; N];
+ let e: [u8; N] = d;
+ let f = [1; N];
+ let g = match f {
+ [a, b, c, d] => a + b + c + d,
+ };
+}
+ "#,
+ );
+}
+
+#[test]
+fn regression_14844_2() {
+ check_no_mismatches(
+ r#"
+//- minicore: fn
+pub const ONE: usize = 1;
+
+pub type MyInner = Inner<ONE>;
+
+pub struct Inner<const P: usize>();
+
+impl Inner<1> {
+ fn map<F>(&self, func: F) -> bool
+ where
+ F: Fn(&MyInner) -> bool,
+ {
+ func(self)
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn dont_crash_on_slice_unsizing() {
+ check_no_mismatches(
+ r#"
+//- minicore: slice, unsize, coerce_unsized
+trait Tr {
+ fn f(self);
+}
+
+impl Tr for [i32] {
+ fn f(self) {
+ let t;
+ x(t);
+ }
+}
+
+fn x(a: [i32; 4]) {
+ let b = a.f();
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 13cc3fea5..a0ff62843 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -854,9 +854,9 @@ fn test2(a1: *const A, a2: *mut A) {
237..239 'a2': *mut A
249..272 '{ ...2.b; }': ()
255..257 'a1': *const A
- 255..259 'a1.b': B
+ 255..259 'a1.b': {unknown}
265..267 'a2': *mut A
- 265..269 'a2.b': B
+ 265..269 'a2.b': {unknown}
"#]],
);
}
@@ -1812,6 +1812,52 @@ fn main() {
//^ [(); 7]
}"#,
);
+ check_types(
+ r#"
+trait Foo {
+ fn x(self);
+}
+
+impl Foo for u8 {
+ fn x(self) {
+ let t = [0; 4 + 2];
+ //^ [i32; 6]
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn const_eval_in_function_signature() {
+ check_types(
+ r#"
+const fn foo() -> usize {
+ 5
+}
+
+fn f() -> [u8; foo()] {
+ loop {}
+}
+
+fn main() {
+ let t = f();
+ //^ [u8; 5]
+}"#,
+ );
+ check_types(
+ r#"
+//- minicore: default, builtin_impls
+fn f() -> [u8; Default::default()] {
+ loop {}
+}
+
+fn main() {
+ let t = f();
+ //^ [u8; 0]
+}
+ "#,
+ );
}
#[test]
@@ -1906,8 +1952,8 @@ fn closure_return() {
"#,
expect![[r#"
16..58 '{ ...; }; }': u32
- 26..27 'x': || -> usize
- 30..55 '|| -> ...n 1; }': || -> usize
+ 26..27 'x': impl Fn() -> usize
+ 30..55 '|| -> ...n 1; }': impl Fn() -> usize
42..55 '{ return 1; }': usize
44..52 'return 1': !
51..52 '1': usize
@@ -1925,8 +1971,8 @@ fn closure_return_unit() {
"#,
expect![[r#"
16..47 '{ ...; }; }': u32
- 26..27 'x': || -> ()
- 30..44 '|| { return; }': || -> ()
+ 26..27 'x': impl Fn()
+ 30..44 '|| { return; }': impl Fn()
33..44 '{ return; }': ()
35..41 'return': !
"#]],
@@ -1943,8 +1989,8 @@ fn closure_return_inferred() {
"#,
expect![[r#"
16..46 '{ ..." }; }': u32
- 26..27 'x': || -> &str
- 30..43 '|| { "test" }': || -> &str
+ 26..27 'x': impl Fn() -> &str
+ 30..43 '|| { "test" }': impl Fn() -> &str
33..43 '{ "test" }': &str
35..41 '"test"': &str
"#]],
@@ -2034,6 +2080,56 @@ fn test() {
}
#[test]
+fn tuple_pattern_nested_match_ergonomics() {
+ check_no_mismatches(
+ r#"
+fn f(x: (&i32, &i32)) -> i32 {
+ match x {
+ (3, 4) => 5,
+ _ => 12,
+ }
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f(x: (&&&&i32, &&&i32)) {
+ let f = match x {
+ t @ (3, 4) => t,
+ _ => loop {},
+ };
+ f;
+ //^ (&&&&i32, &&&i32)
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f() {
+ let x = &&&(&&&2, &&&&&3);
+ let (y, z) = x;
+ //^ &&&&i32
+ let t @ (y, z) = x;
+ t;
+ //^ &&&(&&&i32, &&&&&i32)
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f() {
+ let x = &&&(&&&2, &&&&&3);
+ let (y, z) = x;
+ //^ &&&&i32
+ let t @ (y, z) = x;
+ t;
+ //^ &&&(&&&i32, &&&&&i32)
+}
+ "#,
+ );
+}
+
+#[test]
fn fn_pointer_return() {
check_infer(
r#"
@@ -2050,7 +2146,7 @@ fn fn_pointer_return() {
47..120 '{ ...hod; }': ()
57..63 'vtable': Vtable
66..90 'Vtable...| {} }': Vtable
- 83..88 '|| {}': || -> ()
+ 83..88 '|| {}': impl Fn()
86..88 '{}': ()
100..101 'm': fn()
104..110 'vtable': Vtable
@@ -2087,6 +2183,7 @@ async fn main() {
136..138 '()': ()
150..151 'w': i32
154..166 'const { 92 }': i32
+ 154..166 'const { 92 }': i32
162..164 '92': i32
176..177 't': i32
180..190 ''a: { 92 }': i32
@@ -2094,6 +2191,24 @@ async fn main() {
"#]],
)
}
+
+#[test]
+fn async_fn_and_try_operator() {
+ check_no_mismatches(
+ r#"
+//- minicore: future, result, fn, try, from
+async fn foo() -> Result<(), ()> {
+ Ok(())
+}
+
+async fn bar() -> Result<(), ()> {
+ let x = foo().await?;
+ Ok(x)
+}
+ "#,
+ )
+}
+
#[test]
fn async_block_early_return() {
check_infer(
@@ -2124,9 +2239,9 @@ fn main() {
149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
149..155 'Ok(())': Result<(), ()>
152..154 '()': ()
- 167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
+ 167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
167..228 'test(|... })': ()
- 172..227 '|| asy... }': || -> impl Future<Output = Result<(), ()>>
+ 172..227 '|| asy... }': impl Fn() -> impl Future<Output = Result<(), ()>>
175..227 'async ... }': impl Future<Output = Result<(), ()>>
191..205 'return Err(())': !
198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2252,8 +2367,8 @@ fn infer_labelled_break_with_val() {
"#,
expect![[r#"
9..335 '{ ... }; }': ()
- 19..21 '_x': || -> bool
- 24..332 '|| 'ou... }': || -> bool
+ 19..21 '_x': impl Fn() -> bool
+ 24..332 '|| 'ou... }': impl Fn() -> bool
27..332 ''outer... }': bool
40..332 '{ ... }': ()
54..59 'inner': i8
@@ -2678,6 +2793,179 @@ impl B for Astruct {}
}
#[test]
+fn capture_kinds_simple() {
+ check_types(
+ r#"
+struct S;
+
+impl S {
+ fn read(&self) -> &S { self }
+ fn write(&mut self) -> &mut S { self }
+ fn consume(self) -> S { self }
+}
+
+fn f() {
+ let x = S;
+ let c1 = || x.read();
+ //^^ impl Fn() -> &S
+ let c2 = || x.write();
+ //^^ impl FnMut() -> &mut S
+ let c3 = || x.consume();
+ //^^ impl FnOnce() -> S
+ let c3 = || x.consume().consume().consume();
+ //^^ impl FnOnce() -> S
+ let c3 = || x.consume().write().read();
+ //^^ impl FnOnce() -> &S
+ let x = &mut x;
+ let c1 = || x.write();
+ //^^ impl FnMut() -> &mut S
+ let x = S;
+ let c1 = || { let ref t = x; t };
+ //^^ impl Fn() -> &S
+ let c2 = || { let ref mut t = x; t };
+ //^^ impl FnMut() -> &mut S
+ let c3 = || { let t = x; t };
+ //^^ impl FnOnce() -> S
+}
+ "#,
+ )
+}
+
+#[test]
+fn capture_kinds_closure() {
+ check_types(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ let mut x = 2;
+ x = 5;
+ let mut c1 = || { x = 3; x };
+ //^^^^^^ impl FnMut() -> i32
+ let mut c2 = || { c1() };
+ //^^^^^^ impl FnMut() -> i32
+ let mut c1 = || { x };
+ //^^^^^^ impl Fn() -> i32
+ let mut c2 = || { c1() };
+ //^^^^^^ impl Fn() -> i32
+ struct X;
+ let x = X;
+ let mut c1 = || { x };
+ //^^^^^^ impl FnOnce() -> X
+ let mut c2 = || { c1() };
+ //^^^^^^ impl FnOnce() -> X
+}
+ "#,
+ );
+}
+
+#[test]
+fn capture_kinds_overloaded_deref() {
+ check_types(
+ r#"
+//- minicore: fn, deref_mut
+use core::ops::{Deref, DerefMut};
+
+struct Foo;
+impl Deref for Foo {
+ type Target = (i32, u8);
+ fn deref(&self) -> &(i32, u8) {
+ &(5, 2)
+ }
+}
+impl DerefMut for Foo {
+ fn deref_mut(&mut self) -> &mut (i32, u8) {
+ &mut (5, 2)
+ }
+}
+fn test() {
+ let mut x = Foo;
+ let c1 = || *x;
+ //^^ impl Fn() -> (i32, u8)
+ let c2 = || { *x = (2, 5); };
+ //^^ impl FnMut()
+ let c3 = || { x.1 };
+ //^^ impl Fn() -> u8
+ let c4 = || { x.1 = 6; };
+ //^^ impl FnMut()
+}
+ "#,
+ );
+}
+
+#[test]
+fn capture_kinds_with_copy_types() {
+ check_types(
+ r#"
+//- minicore: copy, clone, derive
+#[derive(Clone, Copy)]
+struct Copy;
+struct NotCopy;
+#[derive(Clone, Copy)]
+struct Generic<T>(T);
+
+trait Tr {
+ type Assoc;
+}
+
+impl Tr for Copy {
+ type Assoc = NotCopy;
+}
+
+#[derive(Clone, Copy)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+ let a = Copy;
+ let b = NotCopy;
+ let c = Generic(Copy);
+ let d = Generic(NotCopy);
+ let e: AssocGeneric<Copy> = AssocGeneric(NotCopy);
+ let c1 = || a;
+ //^^ impl Fn() -> Copy
+ let c2 = || b;
+ //^^ impl FnOnce() -> NotCopy
+ let c3 = || c;
+ //^^ impl Fn() -> Generic<Copy>
+ let c3 = || d;
+ //^^ impl FnOnce() -> Generic<NotCopy>
+ let c3 = || e;
+ //^^ impl FnOnce() -> AssocGeneric<Copy>
+}
+ "#,
+ )
+}
+
+#[test]
+fn derive_macro_should_work_for_associated_type() {
+ check_types(
+ r#"
+//- minicore: copy, clone, derive
+#[derive(Clone)]
+struct X;
+#[derive(Clone)]
+struct Y;
+
+trait Tr {
+ type Assoc;
+}
+
+impl Tr for X {
+ type Assoc = Y;
+}
+
+#[derive(Clone)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+ let e: AssocGeneric<X> = AssocGeneric(Y);
+ let e_clone = e.clone();
+ //^^^^^^^ AssocGeneric<X>
+}
+ "#,
+ )
+}
+
+#[test]
fn cfgd_out_assoc_items() {
check_types(
r#"
@@ -2697,6 +2985,21 @@ fn f() {
}
#[test]
+fn infer_ref_to_raw_cast() {
+ check_types(
+ r#"
+struct S;
+
+fn f() {
+ let s = &mut S;
+ let s = s as *mut _;
+ //^ *mut S
+}
+ "#,
+ );
+}
+
+#[test]
fn infer_missing_type() {
check_types(
r#"
@@ -3194,6 +3497,22 @@ fn func() {
);
}
+#[test]
+fn pointee_trait() {
+ check_types(
+ r#"
+//- minicore: pointee
+use core::ptr::Pointee;
+fn func() {
+ let x: <u8 as Pointee>::Metadata;
+ //^ ()
+ let x: <[u8] as Pointee>::Metadata;
+ //^ usize
+}
+ "#,
+ );
+}
+
// FIXME
#[test]
fn castable_to() {
@@ -3258,35 +3577,60 @@ fn f<T>(t: Ark<T>) {
);
}
-// FIXME
#[test]
-fn castable_to2() {
- check_infer(
+fn const_dependent_on_local() {
+ check_types(
r#"
-fn func() {
- let x = &0u32 as *const _;
+fn main() {
+ let s = 5;
+ let t = [2; s];
+ //^ [i32; _]
}
"#,
- expect![[r#"
- 10..44 '{ ...t _; }': ()
- 20..21 'x': *const {unknown}
- 24..29 '&0u32': &u32
- 24..41 '&0u32 ...onst _': *const {unknown}
- 25..29 '0u32': u32
- "#]],
);
}
#[test]
fn issue_14275() {
- // FIXME: evaluate const generic
check_types(
r#"
struct Foo<const T: bool>;
fn main() {
const B: bool = false;
let foo = Foo::<B>;
- //^^^ Foo<_>
+ //^^^ Foo<false>
+}
+"#,
+ );
+ check_types(
+ r#"
+struct Foo<const T: bool>;
+impl Foo<true> {
+ fn foo(self) -> u8 { 2 }
+}
+impl Foo<false> {
+ fn foo(self) -> u16 { 5 }
+}
+fn main() {
+ const B: bool = false;
+ let foo: Foo<B> = Foo;
+ let x = foo.foo();
+ //^ u16
+}
+"#,
+ );
+}
+
+#[test]
+fn cstring_literals() {
+ check_types(
+ r#"
+#[lang = "CStr"]
+pub struct CStr;
+
+fn main() {
+ c"ello";
+ //^^^^^^^ &CStr
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index da76d7fd8..97ae732a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -90,7 +90,7 @@ fn infer_async_closure() {
async fn test() {
let f = async move |x: i32| x + 42;
f;
-// ^ |i32| -> impl Future<Output = i32>
+// ^ impl Fn(i32) -> impl Future<Output = i32>
let a = f(4);
a;
// ^ impl Future<Output = i32>
@@ -99,7 +99,7 @@ async fn test() {
// ^ i32
let f = async move || 42;
f;
-// ^ || -> impl Future<Output = i32>
+// ^ impl Fn() -> impl Future<Output = i32>
let a = f();
a;
// ^ impl Future<Output = i32>
@@ -116,7 +116,7 @@ async fn test() {
};
let _: Option<u64> = c().await;
c;
-// ^ || -> impl Future<Output = Option<u64>>
+// ^ impl Fn() -> impl Future<Output = Option<u64>>
}
"#,
);
@@ -206,19 +206,27 @@ fn test() {
fn infer_try_trait() {
check_types(
r#"
-//- minicore: try, result
+//- minicore: try, result, from
fn test() {
let r: Result<i32, u64> = Result::Ok(1);
let v = r?;
v;
} //^ i32
-
-impl<O, E> core::ops::Try for Result<O, E> {
- type Output = O;
- type Error = Result<core::convert::Infallible, E>;
+"#,
+ );
}
-impl<T, E, F: From<E>> core::ops::FromResidual<Result<core::convert::Infallible, E>> for Result<T, F> {}
+#[test]
+fn infer_try_block() {
+ // FIXME: We should test more cases, but it currently doesn't work, since
+ // our labeled block type inference is broken.
+ check_types(
+ r#"
+//- minicore: try, option
+fn test() {
+ let x: Option<_> = try { Some(2)?; };
+ //^ Option<()>
+}
"#,
);
}
@@ -542,7 +550,7 @@ fn test() -> u64 {
53..54 'a': S
57..58 'S': S(fn(u32) -> u64) -> S
57..74 'S(|i| ...s u64)': S
- 59..73 '|i| 2*i as u64': |u32| -> u64
+ 59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
60..61 'i': u32
63..64 '2': u64
63..73 '2*i as u64': u64
@@ -1325,9 +1333,9 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
}
"#,
expect![[r#"
- 134..165 '{ ...(C)) }': (|&str, T| -> (), Bar<u8>)
- 140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
- 141..154 '|input, t| {}': |&str, T| -> ()
+ 134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar<u8>)
+ 140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
+ 141..154 '|input, t| {}': impl Fn(&str, T)
142..147 'input': &str
149..150 't': T
152..154 '{}': ()
@@ -1498,8 +1506,8 @@ fn main() {
71..105 '{ ...()); }': ()
77..78 'f': fn f(&dyn Fn(S))
77..102 'f(&|nu...foo())': ()
- 79..101 '&|numb....foo()': &|S| -> ()
- 80..101 '|numbe....foo()': |S| -> ()
+ 79..101 '&|numb....foo()': &impl Fn(S)
+ 80..101 '|numbe....foo()': impl Fn(S)
81..87 'number': S
89..95 'number': S
89..101 'number.foo()': ()
@@ -1904,13 +1912,13 @@ fn test() {
131..132 'f': F
151..153 '{}': Lazy<T, F>
251..497 '{ ...o(); }': ()
- 261..266 'lazy1': Lazy<Foo, || -> Foo>
- 283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
- 283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
- 293..299 '|| Foo': || -> Foo
+ 261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
+ 283..292 'Lazy::new': fn new<Foo, impl Fn() -> Foo>(impl Fn() -> Foo) -> Lazy<Foo, impl Fn() -> Foo>
+ 283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
+ 293..299 '|| Foo': impl Fn() -> Foo
296..299 'Foo': Foo
310..312 'r1': usize
- 315..320 'lazy1': Lazy<Foo, || -> Foo>
+ 315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
315..326 'lazy1.foo()': usize
368..383 'make_foo_fn_ptr': fn() -> Foo
399..410 'make_foo_fn': fn make_foo_fn() -> Foo
@@ -1955,20 +1963,20 @@ fn test() {
163..167 '1u32': u32
174..175 'x': Option<u32>
174..190 'x.map(...v + 1)': Option<u32>
- 180..189 '|v| v + 1': |u32| -> u32
+ 180..189 '|v| v + 1': impl Fn(u32) -> u32
181..182 'v': u32
184..185 'v': u32
184..189 'v + 1': u32
188..189 '1': u32
196..197 'x': Option<u32>
196..212 'x.map(... 1u64)': Option<u64>
- 202..211 '|_v| 1u64': |u32| -> u64
+ 202..211 '|_v| 1u64': impl Fn(u32) -> u64
203..205 '_v': u32
207..211 '1u64': u64
222..223 'y': Option<i64>
239..240 'x': Option<u32>
239..252 'x.map(|_v| 1)': Option<i64>
- 245..251 '|_v| 1': |u32| -> i64
+ 245..251 '|_v| 1': impl Fn(u32) -> i64
246..248 '_v': u32
250..251 '1': i64
"#]],
@@ -1997,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
//^^^^ u64
let g = |v| v + 1;
//^^^^^ u64
- //^^^^^^^^^ |u64| -> u64
+ //^^^^^^^^^ impl Fn(u64) -> u64
g(1u64);
//^^^^^^^ u64
let h = |v| 1u128 + v;
- //^^^^^^^^^^^^^ |u128| -> u128
+ //^^^^^^^^^^^^^ impl Fn(u128) -> u128
}"#,
);
}
@@ -2054,17 +2062,17 @@ fn test() {
312..314 '{}': ()
330..489 '{ ... S); }': ()
340..342 'x1': u64
- 345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
+ 345..349 'foo1': fn foo1<S, u64, impl Fn(S) -> u64>(S, impl Fn(S) -> u64) -> u64
345..368 'foo1(S...hod())': u64
350..351 'S': S
- 353..367 '|s| s.method()': |S| -> u64
+ 353..367 '|s| s.method()': impl Fn(S) -> u64
354..355 's': S
357..358 's': S
357..367 's.method()': u64
378..380 'x2': u64
- 383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
+ 383..387 'foo2': fn foo2<S, u64, impl Fn(S) -> u64>(impl Fn(S) -> u64, S) -> u64
383..406 'foo2(|...(), S)': u64
- 388..402 '|s| s.method()': |S| -> u64
+ 388..402 '|s| s.method()': impl Fn(S) -> u64
389..390 's': S
392..393 's': S
392..402 's.method()': u64
@@ -2073,14 +2081,14 @@ fn test() {
421..422 'S': S
421..446 'S.foo1...hod())': u64
428..429 'S': S
- 431..445 '|s| s.method()': |S| -> u64
+ 431..445 '|s| s.method()': impl Fn(S) -> u64
432..433 's': S
435..436 's': S
435..445 's.method()': u64
456..458 'x4': u64
461..462 'S': S
461..486 'S.foo2...(), S)': u64
- 468..482 '|s| s.method()': |S| -> u64
+ 468..482 '|s| s.method()': impl Fn(S) -> u64
469..470 's': S
472..473 's': S
472..482 's.method()': u64
@@ -2554,9 +2562,9 @@ fn main() {
72..74 '_v': F
117..120 '{ }': ()
132..163 '{ ... }); }': ()
- 138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
+ 138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
138..160 'f::<()... z; })': ()
- 149..159 '|z| { z; }': |&()| -> ()
+ 149..159 '|z| { z; }': impl Fn(&())
150..151 'z': &()
153..159 '{ z; }': ()
155..156 'z': &()
@@ -2713,9 +2721,9 @@ fn main() {
983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
983..1000 'Vec::<...:new()': Vec<i32>
983..1012 'Vec::<...iter()': IntoIter<i32>
- 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
+ 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, impl Fn(i32) -> Option<u32>>
983..1101 'Vec::<... y; })': ()
- 1029..1074 '|x| if...None }': |i32| -> Option<u32>
+ 1029..1074 '|x| if...None }': impl Fn(i32) -> Option<u32>
1030..1031 'x': i32
1033..1074 'if x >...None }': Option<u32>
1036..1037 'x': i32
@@ -2728,7 +2736,7 @@ fn main() {
1049..1057 'x as u32': u32
1066..1074 '{ None }': Option<u32>
1068..1072 'None': Option<u32>
- 1090..1100 '|y| { y; }': |u32| -> ()
+ 1090..1100 '|y| { y; }': impl Fn(u32)
1091..1092 'y': u32
1094..1100 '{ y; }': ()
1096..1097 'y': u32
@@ -2971,13 +2979,13 @@ fn foo() {
52..126 '{ ...)(s) }': ()
62..63 's': Option<i32>
66..78 'Option::None': Option<i32>
- 88..89 'f': |Option<i32>| -> ()
- 92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
+ 88..89 'f': impl Fn(Option<i32>)
+ 92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
93..94 'x': Option<i32>
109..111 '{}': ()
117..124 '(&f)(s)': ()
- 118..120 '&f': &|Option<i32>| -> ()
- 119..120 'f': |Option<i32>| -> ()
+ 118..120 '&f': &impl Fn(Option<i32>)
+ 119..120 'f': impl Fn(Option<i32>)
122..123 's': Option<i32>
"#]],
);
@@ -3043,7 +3051,7 @@ impl<T: ?Sized> core::ops::Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
- &self.inner
+ unsafe { &*self.inner }
}
}
@@ -3054,23 +3062,25 @@ fn foo() {
}"#,
expect![[r#"
154..158 'self': &Box<T>
- 166..193 '{ ... }': &T
- 176..187 '&self.inner': &*mut T
- 177..181 'self': &Box<T>
- 177..187 'self.inner': *mut T
- 206..296 '{ ...&s); }': ()
- 216..217 's': Option<i32>
- 220..224 'None': Option<i32>
- 234..235 'f': Box<dyn FnOnce(&Option<i32>)>
- 269..282 'box (|ps| {})': Box<|&Option<i32>| -> ()>
- 274..281 '|ps| {}': |&Option<i32>| -> ()
- 275..277 'ps': &Option<i32>
- 279..281 '{}': ()
- 288..289 'f': Box<dyn FnOnce(&Option<i32>)>
- 288..293 'f(&s)': ()
- 290..292 '&s': &Option<i32>
- 291..292 's': Option<i32>
- 269..282: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
+ 166..205 '{ ... }': &T
+ 176..199 'unsafe...nner }': &T
+ 185..197 '&*self.inner': &T
+ 186..197 '*self.inner': T
+ 187..191 'self': &Box<T>
+ 187..197 'self.inner': *mut T
+ 218..308 '{ ...&s); }': ()
+ 228..229 's': Option<i32>
+ 232..236 'None': Option<i32>
+ 246..247 'f': Box<dyn FnOnce(&Option<i32>)>
+ 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
+ 286..293 '|ps| {}': impl Fn(&Option<i32>)
+ 287..289 'ps': &Option<i32>
+ 291..293 '{}': ()
+ 300..301 'f': Box<dyn FnOnce(&Option<i32>)>
+ 300..305 'f(&s)': ()
+ 302..304 '&s': &Option<i32>
+ 303..304 's': Option<i32>
+ 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
"#]],
);
}
@@ -3709,7 +3719,6 @@ async fn get_accounts() -> Result<u32, ()> {
#[test]
fn local_impl_1() {
- check!(block_local_impls);
check_types(
r#"
trait Trait<T> {
@@ -3731,7 +3740,6 @@ fn test() {
#[test]
fn local_impl_2() {
- check!(block_local_impls);
check_types(
r#"
struct S;
@@ -3753,7 +3761,6 @@ fn test() {
#[test]
fn local_impl_3() {
- check!(block_local_impls);
check_types(
r#"
trait Trait<T> {
@@ -3778,6 +3785,62 @@ fn test() {
}
#[test]
+fn foreign_trait_with_local_trait_impl() {
+ check!(block_local_impls);
+ check(
+ r#"
+mod module {
+ pub trait T {
+ const C: usize;
+ fn f(&self);
+ }
+}
+
+fn f() {
+ use module::T;
+ impl T for usize {
+ const C: usize = 0;
+ fn f(&self) {}
+ }
+ 0usize.f();
+ //^^^^^^^^^^ type: ()
+ usize::C;
+ //^^^^^^^^type: usize
+}
+"#,
+ );
+}
+
+#[test]
+fn regression_14443_trait_solve() {
+ check_no_mismatches(
+ r#"
+trait T {
+ fn f(&self) {}
+}
+
+
+fn main() {
+ struct A;
+ impl T for A {}
+
+ let a = A;
+
+ let b = {
+ struct B;
+ impl T for B {}
+
+ B
+ };
+
+ a.f();
+ b.f();
+}
+"#,
+ )
+}
+
+#[test]
fn associated_type_sized_bounds() {
check_infer(
r#"
@@ -4149,3 +4212,201 @@ fn test() {
"#,
);
}
+
+#[test]
+fn associated_type_in_struct_expr_path() {
+ // FIXME: All annotation should be resolvable.
+ // For lines marked as unstable, see rust-lang/rust#86935.
+ // FIXME: Remove the comments once stablized.
+ check_types(
+ r#"
+trait Trait {
+ type Assoc;
+ fn f();
+}
+
+struct S { x: u32 }
+
+impl Trait for () {
+ type Assoc = S;
+
+ fn f() {
+ let x = 42;
+ let a = Self::Assoc { x };
+ // ^ S
+ let a = <Self>::Assoc { x }; // unstable
+ // ^ {unknown}
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = S { x };
+ if let Self::Assoc { x } = value {}
+ // ^ u32
+ if let <Self>::Assoc { x } = value {} // unstable
+ // ^ {unknown}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn associated_type_in_struct_expr_path_enum() {
+ // FIXME: All annotation should be resolvable.
+ // For lines marked as unstable, see rust-lang/rust#86935.
+ // FIXME: Remove the comments once stablized.
+ check_types(
+ r#"
+trait Trait {
+ type Assoc;
+ fn f();
+}
+
+enum E {
+ Unit,
+ Struct { x: u32 },
+}
+
+impl Trait for () {
+ type Assoc = E;
+
+ fn f() {
+ let a = Self::Assoc::Unit;
+ // ^ E
+ let a = <Self>::Assoc::Unit;
+ // ^ E
+ let a = <Self::Assoc>::Unit;
+ // ^ E
+ let a = <<Self>::Assoc>::Unit;
+ // ^ E
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = E::Unit;
+ if let Self::Assoc::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^ E
+ if let <Self>::Assoc::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^ E
+ if let <Self::Assoc>::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^ E
+ if let <<Self>::Assoc>::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^^^ E
+
+ let x = 42;
+ let a = Self::Assoc::Struct { x };
+ // ^ E
+ let a = <Self>::Assoc::Struct { x }; // unstable
+ // ^ {unknown}
+ let a = <Self::Assoc>::Struct { x }; // unstable
+ // ^ {unknown}
+ let a = <<Self>::Assoc>::Struct { x }; // unstable
+ // ^ {unknown}
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = E::Struct { x: 42 };
+ if let Self::Assoc::Struct { x } = value {}
+ // ^ u32
+ if let <Self>::Assoc::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ if let <Self::Assoc>::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ if let <<Self>::Assoc>::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn derive_macro_bounds() {
+ check_types(
+ r#"
+ //- minicore: clone, derive
+ #[derive(Clone)]
+ struct Copy;
+ struct NotCopy;
+ #[derive(Clone)]
+ struct Generic<T>(T);
+ trait Tr {
+ type Assoc;
+ }
+ impl Tr for Copy {
+ type Assoc = NotCopy;
+ }
+ #[derive(Clone)]
+ struct AssocGeneric<T: Tr>(T::Assoc);
+
+ // Currently rustc does not accept this.
+ // #[derive(Clone)]
+ // struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
+
+ #[derive(Clone)]
+ struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
+
+ #[derive(Clone)]
+ struct Vec<T>();
+
+ #[derive(Clone)]
+ struct R1(Vec<R2>);
+ #[derive(Clone)]
+ struct R2(R1);
+
+ fn f() {
+ let x = (&Copy).clone();
+ //^ Copy
+ let x = (&NotCopy).clone();
+ //^ &NotCopy
+ let x = (&Generic(Copy)).clone();
+ //^ Generic<Copy>
+ let x = (&Generic(NotCopy)).clone();
+ //^ &Generic<NotCopy>
+ let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
+ let x = x.clone();
+ //^ &AssocGeneric<Copy>
+ // let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
+ // let x = x.clone();
+ let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
+ let x = x.clone();
+ //^ &AssocGeneric3<Copy>
+ let x = (&R1(Vec())).clone();
+ //^ R1
+ let x = (&R2(R1(Vec()))).clone();
+ //^ R2
+ }
+ "#,
+ );
+}
+
+#[test]
+fn trait_obligations_should_be_registered_during_path_inference() {
+ check_types(
+ r#"
+//- minicore: fn, from
+struct S<T>(T);
+fn map<T, U, F: FnOnce(T) -> S<U>>(_: T, _: F) -> U { loop {} }
+
+fn test(v: S<i32>) {
+ let res = map(v, Into::into);
+ //^^^ i32
+}
+"#,
+ );
+}
+
+#[test]
+fn fn_obligation_should_be_registered_during_path_inference() {
+ check_types(
+ r#"
+//- minicore: fn, from
+struct S<T>(T);
+impl<T> S<T> {
+ fn foo<U: Into<S<T>>>(_: U) -> Self { loop {} }
+}
+fn map<T, U, F: FnOnce(T) -> U>(_: T, _: F) -> U { loop {} }
+
+fn test(v: S<i32>) {
+ let res = map(v, S::foo);
+ //^^^ S<i32>
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
index b7e6ee674..83814ed0e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
@@ -24,7 +24,8 @@ impl DebugContext<'_> {
AdtId::UnionId(it) => self.0.union_data(it).name.clone(),
AdtId::EnumId(it) => self.0.enum_data(it).name.clone(),
};
- name.fmt(f)
+ name.display(self.0.upcast()).fmt(f)?;
+ Ok(())
}
pub(crate) fn debug_trait_id(
@@ -34,7 +35,8 @@ impl DebugContext<'_> {
) -> Result<(), fmt::Error> {
let trait_: hir_def::TraitId = from_chalk_trait_id(id);
let trait_data = self.0.trait_data(trait_);
- trait_data.name.fmt(f)
+ trait_data.name.display(self.0.upcast()).fmt(f)?;
+ Ok(())
}
pub(crate) fn debug_assoc_type_id(
@@ -49,7 +51,13 @@ impl DebugContext<'_> {
_ => panic!("associated type not in trait"),
};
let trait_data = self.0.trait_data(trait_);
- write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
+ write!(
+ fmt,
+ "{}::{}",
+ trait_data.name.display(self.0.upcast()),
+ type_alias_data.name.display(self.0.upcast())
+ )?;
+ Ok(())
}
pub(crate) fn debug_projection_ty(
@@ -67,7 +75,7 @@ impl DebugContext<'_> {
let trait_ref = projection_ty.trait_ref(self.0);
let trait_params = trait_ref.substitution.as_slice(Interner);
let self_ty = trait_ref.self_type_parameter(Interner);
- write!(fmt, "<{self_ty:?} as {trait_name}")?;
+ write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?;
if trait_params.len() > 1 {
write!(
fmt,
@@ -75,7 +83,7 @@ impl DebugContext<'_> {
trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))),
)?;
}
- write!(fmt, ">::{}", type_alias_data.name)?;
+ write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?;
let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
@@ -105,9 +113,9 @@ impl DebugContext<'_> {
}
};
match def {
- CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"),
+ CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())),
CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
- write!(fmt, "{{ctor {name}}}")
+ write!(fmt, "{{ctor {}}}", name.display(self.0.upcast()))
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 3ab85c68f..f40b7db3a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -1,22 +1,24 @@
//! Trait solving using Chalk.
-use std::{env::var, sync::Arc};
+use std::env::var;
-use chalk_ir::GoalData;
+use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData};
use chalk_recursive::Cache;
-use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
+use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
use base_db::CrateId;
use hir_def::{
lang_item::{LangItem, LangItemTarget},
- TraitId,
+ BlockId, TraitId,
};
+use hir_expand::name::{name, Name};
use stdx::panic_context;
+use triomphe::Arc;
use crate::{
- db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
- Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty,
- TyKind, WhereClause,
+ db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq,
+ AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy,
+ ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause,
};
/// This controls how much 'time' we give the Chalk solver before giving up.
@@ -26,6 +28,7 @@ const CHALK_SOLVER_FUEL: i32 = 1000;
pub(crate) struct ChalkContext<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) krate: CrateId,
+ pub(crate) block: Option<BlockId>,
}
fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
@@ -43,6 +46,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
pub krate: CrateId,
+ pub block: Option<BlockId>,
// FIXME make this a BTreeMap
pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
pub env: chalk_ir::Environment<Interner>,
@@ -52,6 +56,7 @@ impl TraitEnvironment {
pub fn empty(krate: CrateId) -> Self {
TraitEnvironment {
krate,
+ block: None,
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(Interner),
}
@@ -78,11 +83,12 @@ pub(crate) fn normalize_projection_query(
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: Canonical<InEnvironment<Goal>>,
) -> Option<Solution> {
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(Interner) {
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
- db.trait_data(it.hir_trait_id()).name.to_string()
+ db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string()
}
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
_ => "??".to_string(),
@@ -100,18 +106,25 @@ pub(crate) fn trait_solve_query(
}
}
+ // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So
+ // we should get rid of it when talking to chalk.
+ let goal = goal
+ .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST)
+ .unwrap();
+
// We currently don't deal with universes (I think / hope they're not yet
// relevant for our use cases?)
let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 };
- solve(db, krate, &u_canonical)
+ solve(db, krate, block, &u_canonical)
}
fn solve(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
) -> Option<chalk_solve::Solution<Interner>> {
- let context = ChalkContext { db, krate };
+ let context = ChalkContext { db, krate, block };
tracing::debug!("solve goal: {:?}", goal);
let mut solver = create_chalk_solver();
@@ -171,8 +184,10 @@ fn is_chalk_print() -> bool {
std::env::var("CHALK_PRINT").is_ok()
}
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum FnTrait {
+ // Warning: Order is important. If something implements `x` it should also implement
+ // `y` if `y <= x`.
FnOnce,
FnMut,
Fn,
@@ -187,7 +202,23 @@ impl FnTrait {
}
}
- pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
+ pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
+ match self {
+ FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
+ FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
+ FnTrait::Fn => rust_ir::ClosureKind::Fn,
+ }
+ }
+
+ pub fn method_name(self) -> Name {
+ match self {
+ FnTrait::FnOnce => name!(call_once),
+ FnTrait::FnMut => name!(call_mut),
+ FnTrait::Fn => name!(call),
+ }
+ }
+
+ pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
let target = db.lang_item(krate, self.lang_item())?;
match target {
LangItemTarget::Trait(t) => Some(t),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 34d957e26..363658063 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -1,10 +1,14 @@
//! Helper functions for working with def, which don't need to be a separate
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
-use std::iter;
+use std::{hash::Hash, iter};
use base_db::CrateId;
-use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
+use chalk_ir::{
+ cast::Cast,
+ fold::{FallibleTypeFolder, Shift},
+ BoundVar, DebruijnIndex,
+};
use either::Either;
use hir_def::{
db::DefDatabase,
@@ -15,16 +19,23 @@ use hir_def::{
lang_item::LangItem,
resolver::{HasResolver, TypeNs},
type_ref::{TraitBoundModifier, TypeRef},
- ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId,
- TypeOrConstParamId, TypeParamId,
+ ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId,
+ LocalEnumVariantId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
+ TypeParamId,
};
use hir_expand::name::Name;
use intern::Interned;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
+use stdx::never;
use crate::{
- db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
+ consteval::unknown_const,
+ db::HirDatabase,
+ layout::{Layout, TagEncoding},
+ mir::pad16,
+ ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt,
+ Ty, WhereClause,
};
pub(crate) fn fn_traits(
@@ -69,9 +80,7 @@ pub(super) fn all_super_trait_refs<T>(
cb: impl FnMut(TraitRef) -> Option<T>,
) -> Option<T> {
let seen = iter::once(trait_ref.trait_id).collect();
- let mut stack = Vec::new();
- stack.push(trait_ref);
- SuperTraits { db, seen, stack }.find_map(cb)
+ SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb)
}
struct SuperTraits<'a> {
@@ -130,7 +139,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
WherePredicate::Lifetime { .. } => None,
})
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
- .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
+ .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
Some(TypeNs::TraitId(t)) => Some(t),
_ => None,
})
@@ -176,6 +185,37 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
Generics { def, params: db.generic_params(def), parent_generics }
}
+/// It is a bit different from the rustc equivalent. Currently it stores:
+/// - 0: the function signature, encoded as a function pointer type
+/// - 1..n: generics of the parent
+///
+/// and it doesn't store the closure types and fields.
+///
+/// Codes should not assume this ordering, and should always use methods available
+/// on this struct for retriving, and `TyBuilder::substs_for_closure` for creating.
+pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
+
+impl<'a> ClosureSubst<'a> {
+ pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
+ match self.0.as_slice(Interner) {
+ [_, x @ ..] => x,
+ _ => {
+ never!("Closure missing parameter");
+ &[]
+ }
+ }
+ }
+
+ pub(crate) fn sig_ty(&self) -> &'a Ty {
+ match self.0.as_slice(Interner) {
+ [x, ..] => x.assert_ty_ref(Interner),
+ _ => {
+ unreachable!("Closure missing sig_ty parameter");
+ }
+ }
+ }
+}
+
#[derive(Debug)]
pub(crate) struct Generics {
def: GenericDefId,
@@ -354,3 +394,99 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
_ => false,
}
}
+
+pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> {
+ pub(crate) db: &'a dyn HirDatabase,
+}
+
+impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
+ type Error = ();
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = ()> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_const(
+ &mut self,
+ constant: Const,
+ _outer_binder: DebruijnIndex,
+ ) -> Result<Const, Self::Error> {
+ if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value {
+ if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned {
+ if let Ok(eval) = self.db.const_eval(*id, subst.clone()) {
+ return Ok(eval);
+ } else {
+ return Ok(unknown_const(constant.data(Interner).ty.clone()));
+ }
+ }
+ }
+ Ok(constant)
+ }
+}
+
+pub(crate) fn detect_variant_from_bytes<'a>(
+ layout: &'a Layout,
+ db: &dyn HirDatabase,
+ krate: CrateId,
+ b: &[u8],
+ e: EnumId,
+) -> Option<(LocalEnumVariantId, &'a Layout)> {
+ let (var_id, var_layout) = match &layout.variants {
+ hir_def::layout::Variants::Single { index } => (index.0, &*layout),
+ hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
+ let target_data_layout = db.target_data_layout(krate)?;
+ let size = tag.size(&*target_data_layout).bytes_usize();
+ let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+ let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
+ match tag_encoding {
+ TagEncoding::Direct => {
+ let x = variants.iter_enumerated().find(|x| {
+ db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 })
+ == Ok(tag)
+ })?;
+ (x.0 .0, x.1)
+ }
+ TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+ let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
+ let variant = variants
+ .iter_enumerated()
+ .map(|(x, _)| x)
+ .filter(|x| x != untagged_variant)
+ .nth(candidate_tag)
+ .unwrap_or(*untagged_variant);
+ (variant.0, &variants[variant])
+ }
+ }
+ }
+ };
+ Some((var_id, var_layout))
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct InTypeConstIdMetadata(pub(crate) Ty);
+
+impl OpaqueInternableThing for InTypeConstIdMetadata {
+ fn dyn_hash(&self, mut state: &mut dyn std::hash::Hasher) {
+ self.hash(&mut state);
+ }
+
+ fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool {
+ other.as_any().downcast_ref::<Self>().map_or(false, |x| self == x)
+ }
+
+ fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing> {
+ Box::new(self.clone())
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
+ fn box_any(&self) -> Box<dyn std::any::Any> {
+ Box::new(self.clone())
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index ef40a8902..a20aff93f 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -17,6 +17,7 @@ either = "1.7.0"
arrayvec = "0.7.2"
itertools = "0.10.5"
smallvec.workspace = true
+triomphe.workspace = true
once_cell = "1.17.0"
# local deps
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index db0b84ef0..b81793729 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -4,7 +4,6 @@ use hir_def::{
attr::{AttrsWithOwner, Documentation},
item_scope::ItemInNs,
path::ModPath,
- per_ns::PerNs,
resolver::HasResolver,
AttrDefId, GenericParamId, ModuleDefId,
};
@@ -41,7 +40,7 @@ macro_rules! impl_has_attrs {
impl HasAttrs for $def {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
let def = AttrDefId::$def_id(self.into());
- db.attrs(def)
+ db.attrs_with_owner(def)
}
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
let def = AttrDefId::$def_id(self.into());
@@ -121,6 +120,7 @@ impl HasAttrs for AssocItem {
}
}
+/// Resolves the item `link` points to in the scope of `def`.
fn resolve_doc_path(
db: &dyn HirDatabase,
def: AttrDefId,
@@ -155,14 +155,14 @@ fn resolve_doc_path(
.syntax_node()
.descendants()
.find_map(ast::Path::cast)?;
- if ast_path.to_string() != link {
+ if ast_path.syntax().text() != link {
return None;
}
ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
};
let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
- let resolved = if resolved == PerNs::none() {
+ let resolved = if resolved.is_none() {
resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
} else {
resolved
diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs
index 0935b5ea5..e0cde689f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/db.rs
@@ -6,8 +6,8 @@
pub use hir_def::db::*;
pub use hir_expand::db::{
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
- InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
- MacroExpandQuery, ParseMacroExpansionQuery,
+ InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery,
+ ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery,
};
pub use hir_ty::db::*;
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 253d62daf..b64d81490 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -10,7 +10,7 @@ use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::path::ModPath;
use hir_expand::{name::Name, HirFileId, InFile};
-use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
+use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
use crate::{AssocItem, Field, Local, MacroKind, Type};
@@ -38,19 +38,25 @@ diagnostics![
IncorrectCase,
InvalidDeriveTarget,
IncoherentImpl,
+ MacroDefError,
MacroError,
+ MacroExpansionParseError,
MalformedDerive,
MismatchedArgCount,
MissingFields,
MissingMatchArms,
MissingUnsafe,
+ MovedOutOfRef,
NeedMut,
NoSuchField,
PrivateAssocItem,
PrivateField,
ReplaceFilterMapNextWithFindMap,
+ TypedHole,
TypeMismatch,
+ UndeclaredLabel,
UnimplementedBuiltinMacro,
+ UnreachableLabel,
UnresolvedExternCrate,
UnresolvedField,
UnresolvedImport,
@@ -62,6 +68,19 @@ diagnostics![
];
#[derive(Debug)]
+pub struct BreakOutsideOfLoop {
+ pub expr: InFile<AstPtr<ast::Expr>>,
+ pub is_break: bool,
+ pub bad_value_break: bool,
+}
+
+#[derive(Debug)]
+pub struct TypedHole {
+ pub expr: InFile<AstPtr<ast::Expr>>,
+ pub expected: Type,
+}
+
+#[derive(Debug)]
pub struct UnresolvedModule {
pub decl: InFile<AstPtr<ast::Module>>,
pub candidates: Box<[String]>,
@@ -84,6 +103,17 @@ pub struct UnresolvedMacroCall {
pub path: ModPath,
pub is_bang: bool,
}
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UnreachableLabel {
+ pub node: InFile<AstPtr<ast::Lifetime>>,
+ pub name: Name,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UndeclaredLabel {
+ pub node: InFile<AstPtr<ast::Lifetime>>,
+ pub name: Name,
+}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InactiveCode {
@@ -111,6 +141,20 @@ pub struct MacroError {
pub message: String,
}
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct MacroExpansionParseError {
+ pub node: InFile<SyntaxNodePtr>,
+ pub precise_location: Option<TextRange>,
+ pub errors: Box<[SyntaxError]>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct MacroDefError {
+ pub node: InFile<AstPtr<ast::Macro>>,
+ pub message: String,
+ pub name: Option<TextRange>,
+}
+
#[derive(Debug)]
pub struct UnimplementedBuiltinMacro {
pub node: InFile<SyntaxNodePtr>,
@@ -167,13 +211,6 @@ pub struct PrivateField {
}
#[derive(Debug)]
-pub struct BreakOutsideOfLoop {
- pub expr: InFile<AstPtr<ast::Expr>>,
- pub is_break: bool,
- pub bad_value_break: bool,
-}
-
-#[derive(Debug)]
pub struct MissingUnsafe {
pub expr: InFile<AstPtr<ast::Expr>>,
}
@@ -223,3 +260,9 @@ pub struct NeedMut {
pub struct UnusedMut {
pub local: Local,
}
+
+#[derive(Debug)]
+pub struct MovedOutOfRef {
+ pub ty: Type,
+ pub span: InFile<SyntaxNodePtr>,
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 5aae92efd..9a2090ab7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -1,6 +1,6 @@
//! HirDisplay implementations for various hir types.
use hir_def::{
- adt::VariantData,
+ data::adt::VariantData,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
@@ -8,6 +8,7 @@ use hir_def::{
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
+use hir_expand::name;
use hir_ty::{
display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -50,7 +51,7 @@ impl HirDisplay for Function {
// FIXME: String escape?
write!(f, "extern \"{}\" ", &**abi)?;
}
- write!(f, "fn {}", data.name)?;
+ write!(f, "fn {}", data.name.display(f.db.upcast()))?;
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
@@ -62,7 +63,7 @@ impl HirDisplay for Function {
{
f.write_char('&')?;
if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name)?;
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
}
if let hir_def::type_ref::Mutability::Mut = mut_ {
f.write_str("mut ")?;
@@ -76,22 +77,22 @@ impl HirDisplay for Function {
};
let mut first = true;
- for (name, type_ref) in &data.params {
+ // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
+ for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+ let local = param.as_local(db).map(|it| it.name(db));
if !first {
f.write_str(", ")?;
} else {
first = false;
- if data.has_self_param() {
+ if local == Some(name!(self)) {
write_self_param(type_ref, f)?;
continue;
}
}
- match name {
- Some(name) => write!(f, "{name}: ")?,
+ match local {
+ Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
None => f.write_str("_: ")?,
}
- // FIXME: Use resolved `param.ty` or raw `type_ref`?
- // The former will ignore lifetime arguments currently.
type_ref.hir_fmt(f)?;
}
@@ -150,7 +151,7 @@ impl HirDisplay for Struct {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("struct ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -162,7 +163,7 @@ impl HirDisplay for Enum {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("enum ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -174,7 +175,7 @@ impl HirDisplay for Union {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("union ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -185,14 +186,14 @@ impl HirDisplay for Union {
impl HirDisplay for Field {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
- write!(f, "{}: ", self.name(f.db))?;
+ write!(f, "{}: ", self.name(f.db).display(f.db.upcast()))?;
self.ty(f.db).hir_fmt(f)
}
}
impl HirDisplay for Variant {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let data = self.variant_data(f.db);
match &*data {
VariantData::Unit => {}
@@ -221,7 +222,7 @@ impl HirDisplay for Variant {
f.write_str(", ")?;
}
// Enum variant fields must be pub.
- write!(f, "{}: ", field.name)?;
+ write!(f, "{}: ", field.name.display(f.db.upcast()))?;
field.type_ref.hir_fmt(f)?;
}
f.write_str(" }")?;
@@ -258,7 +259,7 @@ impl HirDisplay for TypeOrConstParam {
impl HirDisplay for TypeParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
if f.omit_verbose_types() {
return Ok(());
}
@@ -285,13 +286,13 @@ impl HirDisplay for TypeParam {
impl HirDisplay for LifetimeParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))
}
}
impl HirDisplay for ConstParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "const {}: ", self.name(f.db))?;
+ write!(f, "const {}: ", self.name(f.db).display(f.db.upcast()))?;
self.ty(f.db).hir_fmt(f)
}
}
@@ -324,7 +325,7 @@ fn write_generic_params(
};
for (_, lifetime) in params.lifetimes.iter() {
delim(f)?;
- write!(f, "{}", lifetime.name)?;
+ write!(f, "{}", lifetime.name.display(f.db.upcast()))?;
}
for (_, ty) in params.type_or_consts.iter() {
if let Some(name) = &ty.name() {
@@ -334,7 +335,7 @@ fn write_generic_params(
continue;
}
delim(f)?;
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
if let Some(default) = &ty.default {
f.write_str(" = ")?;
default.hir_fmt(f)?;
@@ -342,7 +343,7 @@ fn write_generic_params(
}
TypeOrConstParamData::ConstParamData(c) => {
delim(f)?;
- write!(f, "const {name}: ")?;
+ write!(f, "const {}: ", name.display(f.db.upcast()))?;
c.ty.hir_fmt(f)?;
}
}
@@ -379,7 +380,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => write!(f, "{name}"),
+ Some(name) => write!(f, "{}", name.display(f.db.upcast())),
None => f.write_str("{unnamed}"),
}
}
@@ -411,10 +412,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
WherePredicate::Lifetime { target, bound } => {
if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target)
{
- write!(f, " + {}", bound.name)?;
+ write!(f, " + {}", bound.name.display(f.db.upcast()))?;
} else {
new_predicate(f)?;
- write!(f, "{}: {}", target.name, bound.name)?;
+ write!(
+ f,
+ "{}: {}",
+ target.name.display(f.db.upcast()),
+ bound.name.display(f.db.upcast())
+ )?;
}
}
WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -431,7 +437,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
if idx != 0 {
f.write_str(", ")?;
}
- write!(f, "{lifetime}")?;
+ write!(f, "{}", lifetime.display(f.db.upcast()))?;
}
f.write_str("> ")?;
write_target(target, f)?;
@@ -461,7 +467,7 @@ impl HirDisplay for Const {
let data = db.const_data(self.id);
f.write_str("const ")?;
match &data.name {
- Some(name) => write!(f, "{name}: ")?,
+ Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
None => f.write_str("_: ")?,
}
data.type_ref.hir_fmt(f)?;
@@ -477,7 +483,7 @@ impl HirDisplay for Static {
if data.mutable {
f.write_str("mut ")?;
}
- write!(f, "{}: ", &data.name)?;
+ write!(f, "{}: ", data.name.display(f.db.upcast()))?;
data.type_ref.hir_fmt(f)?;
Ok(())
}
@@ -493,7 +499,7 @@ impl HirDisplay for Trait {
if data.is_auto {
f.write_str("auto ")?;
}
- write!(f, "trait {}", data.name)?;
+ write!(f, "trait {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TraitId(self.id);
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -505,7 +511,7 @@ impl HirDisplay for TraitAlias {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.trait_alias_data(self.id);
- write!(f, "trait {}", data.name)?;
+ write!(f, "trait {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TraitAliasId(self.id);
write_generic_params(def_id, f)?;
f.write_str(" = ")?;
@@ -521,7 +527,7 @@ impl HirDisplay for TypeAlias {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.type_alias_data(self.id);
- write!(f, "type {}", data.name)?;
+ write!(f, "type {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TypeAliasId(self.id);
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -541,8 +547,8 @@ impl HirDisplay for Module {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
// FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) {
- Some(name) => write!(f, "mod {name}"),
- None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) {
+ Some(name) => write!(f, "mod {}", name.display(f.db.upcast())),
+ None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) {
Some(name) => write!(f, "extern crate {name}"),
None => f.write_str("extern crate {unknown}"),
},
@@ -558,6 +564,6 @@ impl HirDisplay for Macro {
hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"),
hir_def::MacroId::ProcMacroId(_) => f.write_str("proc_macro"),
}?;
- write!(f, " {}", self.name(f.db))
+ write!(f, " {}", self.name(f.db).display(f.db.upcast()))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index aaaa7abf3..de2390219 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -4,7 +4,7 @@
//! are splitting the hir.
use hir_def::{
- expr::{BindingId, LabelId},
+ hir::{BindingId, LabelId},
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
ModuleDefId, VariantId,
};
@@ -40,6 +40,7 @@ from_id![
(hir_def::TraitAliasId, crate::TraitAlias),
(hir_def::StaticId, crate::Static),
(hir_def::ConstId, crate::Const),
+ (hir_def::InTypeConstId, crate::InTypeConst),
(hir_def::FunctionId, crate::Function),
(hir_def::ImplId, crate::Impl),
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
@@ -144,6 +145,7 @@ impl From<DefWithBody> for DefWithBodyId {
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
+ DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id),
}
}
}
@@ -155,6 +157,7 @@ impl From<DefWithBodyId> for DefWithBody {
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
+ DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 35424feec..6df625380 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -6,7 +6,7 @@
//! applied. So, the relation between syntax and HIR is many-to-one.
//!
//! HIR is the public API of the all of the compiler logic above syntax trees.
-//! It is written in "OO" style. Each type is self contained (as in, it knows it's
+//! It is written in "OO" style. Each type is self contained (as in, it knows its
//! parents and full context). It should be "clean code".
//!
//! `hir_*` crates are the implementation of the compiler logic.
@@ -33,27 +33,29 @@ pub mod symbols;
mod display;
-use std::{iter, ops::ControlFlow, sync::Arc};
+use std::{iter, ops::ControlFlow};
use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either;
use hir_def::{
- adt::VariantData,
body::{BodyDiagnostic, SyntheticSyntax},
- expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+ data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
+ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
item_tree::ItemTreeNode,
- lang_item::{LangItem, LangItemTarget},
- layout::{Layout, LayoutError, ReprOptions},
+ lang_item::LangItemTarget,
+ layout::{self, ReprOptions, TargetDataLayout},
+ macro_id_to_def_id,
nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
- AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
- EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
- LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
- TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+ AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
+ EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+ LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId,
+ StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+ UnionId,
};
use hir_expand::{name::name, MacroCallKind};
use hir_ty::{
@@ -61,14 +63,15 @@ use hir_ty::{
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
display::HexifiedConst,
- layout::layout_of_ty,
+ layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
primitive::UintTy,
traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
- TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
+ TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
+ WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
@@ -79,6 +82,7 @@ use syntax::{
ast::{self, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T,
};
+use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase};
@@ -86,11 +90,13 @@ pub use crate::{
attrs::{HasAttrs, Namespace},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
- IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
- MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
- PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
- UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
- UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
+ IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
+ MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
+ MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
+ ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
+ UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
+ UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
+ UnresolvedProcMacro, UnusedMut,
},
has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -108,20 +114,18 @@ pub use crate::{
pub use {
cfg::{CfgAtom, CfgExpr, CfgOptions},
hir_def::{
- adt::StructKind,
- attr::{Attrs, AttrsWithOwner, Documentation},
- builtin_attr::AttributeTemplate,
+ attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
+ data::adt::StructKind,
find_path::PrefixKind,
import_map,
- nameres::ModuleSource,
+ lang_item::LangItem,
+ nameres::{DefMap, ModuleSource},
path::{ModPath, PathKind},
type_ref::{Mutability, TypeRef},
visibility::Visibility,
- // FIXME: This is here since it is input of a method in `HirWrite`
- // and things outside of hir need to implement that trait. We probably
- // should move whole `hir_ty::display` to this crate so we will become
- // able to use `ModuleDef` or `Definition` instead of `ModuleDefId`.
- ModuleDefId,
+ // FIXME: This is here since some queries take it as input that are used
+ // outside of hir.
+ {AdtId, ModuleDefId},
},
hir_expand::{
attrs::Attr,
@@ -129,7 +133,8 @@ pub use {
ExpandResult, HirFileId, InFile, MacroFile, Origin,
},
hir_ty::{
- display::{HirDisplay, HirDisplayError, HirWrite},
+ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
+ layout::LayoutError,
mir::MirEvalError,
PointerCast, Safety,
},
@@ -198,7 +203,7 @@ impl Crate {
pub fn root_module(self, db: &dyn HirDatabase) -> Module {
let def_map = db.crate_def_map(self.id);
- Module { id: def_map.module_id(def_map.root()) }
+ Module { id: def_map.crate_root().into() }
}
pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> {
@@ -253,7 +258,8 @@ impl Crate {
}
pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
- db.crate_graph()[self.id].potential_cfg_options.clone()
+ let data = &db.crate_graph()[self.id];
+ data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
}
}
@@ -326,7 +332,7 @@ impl ModuleDef {
segments.extend(m.name(db))
}
segments.reverse();
- Some(segments.into_iter().join("::"))
+ Some(segments.iter().map(|it| it.display(db.upcast())).join("::"))
}
pub fn canonical_module_path(
@@ -470,12 +476,11 @@ impl Module {
/// in the module tree of any target in `Cargo.toml`.
pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
let def_map = db.crate_def_map(self.id.krate());
- Module { id: def_map.module_id(def_map.root()) }
+ Module { id: def_map.crate_root().into() }
}
- pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool {
- let def_map = db.crate_def_map(self.id.krate());
- def_map.root() == self.id.local_id
+ pub fn is_crate_root(self) -> bool {
+ DefMap::ROOT == self.id.local_id
}
/// Iterates over all child modules.
@@ -552,7 +557,11 @@ impl Module {
/// Fills `acc` with the module's diagnostics.
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
let _p = profile::span("Module::diagnostics").detail(|| {
- format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
+ format!(
+ "{:?}",
+ self.name(db)
+ .map_or("<unknown>".into(), |name| name.display(db.upcast()).to_string())
+ )
});
let def_map = self.id.def_map(db.upcast());
for diag in def_map.diagnostics() {
@@ -562,6 +571,7 @@ impl Module {
}
emit_def_diagnostic(db, acc, diag);
}
+
for decl in self.declarations(db) {
match decl {
ModuleDef::Module(m) => {
@@ -600,9 +610,11 @@ impl Module {
}
acc.extend(decl.diagnostics(db))
}
+ ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
_ => acc.extend(decl.diagnostics(db)),
}
}
+ self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
let inherent_impls = db.inherent_impls_in_crate(self.id.krate());
@@ -684,8 +696,31 @@ impl Module {
}
}
+fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) {
+ let id = macro_id_to_def_id(db.upcast(), m.id);
+ if let Err(e) = db.macro_def(id) {
+ let Some(ast) = id.ast_id().left() else {
+ never!("MacroDefError for proc-macro: {:?}", e);
+ return;
+ };
+ emit_def_diagnostic_(
+ db,
+ acc,
+ &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() },
+ );
+ }
+}
+
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
- match &diag.kind {
+ emit_def_diagnostic_(db, acc, &diag.kind)
+}
+
+fn emit_def_diagnostic_(
+ db: &dyn HirDatabase,
+ acc: &mut Vec<AnyDiagnostic>,
+ diag: &DefDiagnosticKind,
+) {
+ match diag {
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
let decl = declaration.to_node(db.upcast());
acc.push(
@@ -725,7 +760,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
acc.push(
@@ -733,7 +767,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(
@@ -746,12 +779,16 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::MacroError { ast, message } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(MacroError { node, precise_location, message: message.clone() }.into());
}
-
+ DefDiagnosticKind::MacroExpansionParseError { ast, errors } => {
+ let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
+ acc.push(
+ MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(),
+ );
+ }
DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
let node = ast.to_node(db.upcast());
// Must have a name, otherwise we wouldn't emit it.
@@ -793,6 +830,17 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
None => stdx::never!("derive diagnostic on item without derive attribute"),
}
}
+ DefDiagnosticKind::MacroDefError { ast, message } => {
+ let node = ast.to_node(db.upcast());
+ acc.push(
+ MacroDefError {
+ node: InFile::new(ast.file_id, AstPtr::new(&node)),
+ name: node.name().map(|it| it.syntax().text_range()),
+ message: message.clone(),
+ }
+ .into(),
+ );
+ }
}
}
@@ -800,7 +848,7 @@ fn precise_macro_call_location(
ast: &MacroCallKind,
db: &dyn HirDatabase,
) -> (InFile<SyntaxNodePtr>, Option<TextRange>, Option<String>, MacroKind) {
- // FIXME: maaybe we actually want slightly different ranges for the different macro diagnostics
+ // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics
// - e.g. the full attribute for macro errors, but only the name for name resolution
match ast {
MacroCallKind::FnLike { ast_id, .. } => {
@@ -915,7 +963,8 @@ impl Field {
}
pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
- layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into())
+ db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into())
+ .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap()))
}
pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
@@ -959,6 +1008,10 @@ impl Struct {
Type::from_def(db, self.id)
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
db.struct_data(self.id).repr
}
@@ -996,6 +1049,10 @@ impl Union {
Type::from_def(db, self.id)
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
db.union_data(self.id)
.variant_data
@@ -1034,6 +1091,10 @@ impl Enum {
db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
}
+ pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
+ db.enum_data(self.id).repr
+ }
+
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::from_def(db, self.id)
}
@@ -1043,7 +1104,7 @@ impl Enum {
Type::new_for_crate(
self.id.lookup(db.upcast()).container.krate(),
TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
- hir_def::layout::IntegerType::Pointer(sign) => match sign {
+ layout::IntegerType::Pointer(sign) => match sign {
true => hir_def::builtin_type::BuiltinType::Int(
hir_def::builtin_type::BuiltinInt::Isize,
),
@@ -1051,29 +1112,34 @@ impl Enum {
hir_def::builtin_type::BuiltinUint::Usize,
),
},
- hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
+ layout::IntegerType::Fixed(i, sign) => match sign {
true => hir_def::builtin_type::BuiltinType::Int(match i {
- hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
- hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
- hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
- hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
- hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
+ layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
+ layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
+ layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
+ layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
+ layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
}),
false => hir_def::builtin_type::BuiltinType::Uint(match i {
- hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
- hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
- hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
- hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
- hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
+ layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
+ layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
+ layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
+ layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
+ layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
}),
},
}),
)
}
+ /// Returns true if at least one variant of this enum is a non-unit variant.
pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool {
self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
}
+
+ pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ Adt::from(self).layout(db)
+ }
}
impl HasVisibility for Enum {
@@ -1103,6 +1169,10 @@ impl Variant {
self.parent
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id })
+ }
+
pub fn name(self, db: &dyn HirDatabase) -> Name {
db.enum_data(self.parent.id).variants[self.id].name.clone()
}
@@ -1130,6 +1200,18 @@ impl Variant {
pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> {
db.const_eval_discriminant(self.into())
}
+
+ pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ let parent_enum = self.parent_enum(db);
+ let parent_layout = parent_enum.layout(db)?;
+ Ok(match &parent_layout.0.variants {
+ layout::Variants::Multiple { variants, .. } => Layout(
+ Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()),
+ db.target_data_layout(parent_enum.krate(db).into()).unwrap(),
+ ),
+ _ => parent_layout,
+ })
+ }
}
/// Variants inherit visibility from the parent enum.
@@ -1161,7 +1243,9 @@ impl Adt {
if db.generic_params(self.into()).iter().count() != 0 {
return Err(LayoutError::HasPlaceholder);
}
- db.layout_of_adt(self.into(), Substitution::empty(Interner))
+ let krate = self.krate(db).id;
+ db.layout_of_adt(self.into(), Substitution::empty(Interner), krate)
+ .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap()))
}
/// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -1292,8 +1376,9 @@ pub enum DefWithBody {
Static(Static),
Const(Const),
Variant(Variant),
+ InTypeConst(InTypeConst),
}
-impl_from!(Function, Const, Static, Variant for DefWithBody);
+impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody);
impl DefWithBody {
pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1302,6 +1387,7 @@ impl DefWithBody {
DefWithBody::Function(f) => f.module(db),
DefWithBody::Static(s) => s.module(db),
DefWithBody::Variant(v) => v.module(db),
+ DefWithBody::InTypeConst(c) => c.module(db),
}
}
@@ -1311,6 +1397,7 @@ impl DefWithBody {
DefWithBody::Static(s) => Some(s.name(db)),
DefWithBody::Const(c) => c.name(db),
DefWithBody::Variant(v) => Some(v.name(db)),
+ DefWithBody::InTypeConst(_) => None,
}
}
@@ -1321,6 +1408,11 @@ impl DefWithBody {
DefWithBody::Static(it) => it.ty(db),
DefWithBody::Const(it) => it.ty(db),
DefWithBody::Variant(it) => it.parent.variant_body_ty(db),
+ DefWithBody::InTypeConst(it) => Type::new_with_resolver_inner(
+ db,
+ &DefWithBodyId::from(it.id).resolver(db.upcast()),
+ TyKind::Error.intern(Interner),
+ ),
}
}
@@ -1330,6 +1422,7 @@ impl DefWithBody {
DefWithBody::Static(it) => it.id.into(),
DefWithBody::Const(it) => it.id.into(),
DefWithBody::Variant(it) => it.into(),
+ DefWithBody::InTypeConst(it) => it.id.into(),
}
}
@@ -1392,6 +1485,12 @@ impl DefWithBody {
}
.into(),
),
+ BodyDiagnostic::UnreachableLabel { node, name } => {
+ acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into())
+ }
+ BodyDiagnostic::UndeclaredLabel { node, name } => {
+ acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into())
+ }
}
}
@@ -1404,14 +1503,6 @@ impl DefWithBody {
let field = source_map.field_syntax(expr);
acc.push(NoSuchField { field }.into())
}
- &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
- expr,
- is_break,
- bad_value_break,
- } => {
- let expr = expr_syntax(expr);
- acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
- }
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
acc.push(
MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
@@ -1483,14 +1574,29 @@ impl DefWithBody {
.into(),
)
}
+ &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+ expr,
+ is_break,
+ bad_value_break,
+ } => {
+ let expr = expr_syntax(expr);
+ acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
+ }
+ hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => {
+ let expr = expr_syntax(*expr);
+ acc.push(
+ TypedHole {
+ expr,
+ expected: Type::new(db, DefWithBodyId::from(self), expected.clone()),
+ }
+ .into(),
+ )
+ }
}
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
let expr_or_pat = match pat_or_expr {
ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
- // FIXME: Re-enable these once we have less false positives
- ExprOrPatId::PatId(_pat) => continue,
- #[allow(unreachable_patterns)]
ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
};
let expr_or_pat = match expr_or_pat {
@@ -1515,7 +1621,7 @@ impl DefWithBody {
match source_map.expr_syntax(expr) {
Ok(expr) => acc.push(MissingUnsafe { expr }.into()),
Err(SyntheticSyntax) => {
- // FIXME: Here and eslwhere in this file, the `expr` was
+ // FIXME: Here and elsewhere in this file, the `expr` was
// desugared, report or assert that this doesn't happen.
}
}
@@ -1523,35 +1629,71 @@ impl DefWithBody {
let hir_body = db.body(self.into());
- if let Ok(borrowck_result) = db.borrowck(self.into()) {
- let mir_body = &borrowck_result.mir_body;
- let mol = &borrowck_result.mutability_of_locals;
- for (binding_id, _) in hir_body.bindings.iter() {
- let need_mut = &mol[mir_body.binding_locals[binding_id]];
- let local = Local { parent: self.into(), binding_id };
- match (need_mut, local.is_mut(db)) {
- (mir::MutabilityReason::Mut { .. }, true)
- | (mir::MutabilityReason::Not, false) => (),
- (mir::MutabilityReason::Mut { spans }, false) => {
- for span in spans {
- let span: InFile<SyntaxNodePtr> = match span {
- mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
- Ok(s) => s.map(|x| x.into()),
- Err(_) => continue,
- },
- mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
- Ok(s) => s.map(|x| match x {
- Either::Left(e) => e.into(),
- Either::Right(e) => e.into(),
- }),
- Err(_) => continue,
- },
- mir::MirSpan::Unknown => continue,
- };
- acc.push(NeedMut { local, span }.into());
+ if let Ok(borrowck_results) = db.borrowck(self.into()) {
+ for borrowck_result in borrowck_results.iter() {
+ let mir_body = &borrowck_result.mir_body;
+ for moof in &borrowck_result.moved_out_of_ref {
+ let span: InFile<SyntaxNodePtr> = match moof.span {
+ mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ mir::MirSpan::Unknown => continue,
+ };
+ acc.push(
+ MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span }
+ .into(),
+ )
+ }
+ let mol = &borrowck_result.mutability_of_locals;
+ for (binding_id, binding_data) in hir_body.bindings.iter() {
+ if binding_data.problems.is_some() {
+ // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
+ continue;
+ }
+ let Some(&local) = mir_body.binding_locals.get(binding_id) else {
+ continue;
+ };
+ let need_mut = &mol[local];
+ let local = Local { parent: self.into(), binding_id };
+ match (need_mut, local.is_mut(db)) {
+ (mir::MutabilityReason::Mut { .. }, true)
+ | (mir::MutabilityReason::Not, false) => (),
+ (mir::MutabilityReason::Mut { spans }, false) => {
+ for span in spans {
+ let span: InFile<SyntaxNodePtr> = match span {
+ mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ mir::MirSpan::Unknown => continue,
+ };
+ acc.push(NeedMut { local, span }.into());
+ }
+ }
+ (mir::MutabilityReason::Not, true) => {
+ if !infer.mutated_bindings_in_closure.contains(&binding_id) {
+ let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_"));
+ if !should_ignore {
+ acc.push(UnusedMut { local }.into())
+ }
+ }
}
}
- (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
}
}
}
@@ -1665,6 +1807,8 @@ impl DefWithBody {
DefWithBody::Static(it) => it.into(),
DefWithBody::Const(it) => it.into(),
DefWithBody::Variant(it) => it.into(),
+ // FIXME: don't ignore diagnostics for in type const
+ DefWithBody::InTypeConst(_) => return,
};
for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
acc.push(diag.into())
@@ -1686,6 +1830,10 @@ impl Function {
db.function_data(self.id).name.clone()
}
+ pub fn ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
/// Get this function's return type
pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
let resolver = self.id.resolver(db.upcast());
@@ -1797,12 +1945,41 @@ impl Function {
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
}
- pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> {
- let body = db
- .mir_body(self.id.into())
- .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
- interpret_mir(db, &body, false)?;
- Ok(())
+ pub fn eval(
+ self,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> String {
+ let body = match db.monomorphized_mir_body(
+ self.id.into(),
+ Substitution::empty(Interner),
+ db.trait_environment(self.id.into()),
+ ) {
+ Ok(body) => body,
+ Err(e) => {
+ let mut r = String::new();
+ _ = e.pretty_print(&mut r, db, &span_formatter);
+ return r;
+ }
+ };
+ let (result, stdout, stderr) = interpret_mir(db, &body, false);
+ let mut text = match result {
+ Ok(_) => "pass".to_string(),
+ Err(e) => {
+ let mut r = String::new();
+ _ = e.pretty_print(&mut r, db, &span_formatter);
+ r
+ }
+ };
+ if !stdout.is_empty() {
+ text += "\n--------- stdout ---------\n";
+ text += &stdout;
+ }
+ if !stderr.is_empty() {
+ text += "\n--------- stderr ---------\n";
+ text += &stderr;
+ }
+ text
}
}
@@ -1837,7 +2014,7 @@ impl Param {
}
pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
- db.function_data(self.func.id).params[self.idx].0.clone()
+ Some(self.as_local(db)?.name(db))
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
@@ -1878,7 +2055,7 @@ impl SelfParam {
func_data
.params
.first()
- .map(|(_, param)| match &**param {
+ .map(|param| match &**param {
TypeRef::Reference(.., mutability) => match mutability {
hir_def::type_ref::Mutability::Shared => Access::Shared,
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
@@ -1921,6 +2098,17 @@ impl HasVisibility for Function {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InTypeConst {
+ pub(crate) id: InTypeConstId,
+}
+
+impl InTypeConst {
+ pub fn module(self, db: &dyn HirDatabase) -> Module {
+ Module { id: self.id.lookup(db.upcast()).owner.module(db.upcast()) }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Const {
pub(crate) id: ConstId,
}
@@ -1939,24 +2127,12 @@ impl Const {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type {
- let data = db.const_data(self.id);
- let resolver = self.id.resolver(db.upcast());
- let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
- let ty = ctx.lower_ty(&data.type_ref);
- Type::new_with_resolver_inner(db, &resolver, ty)
+ Type::from_value_def(db, self.id)
}
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
- let c = db.const_eval(self.id)?;
+ let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
let r = format!("{}", HexifiedConst(c).display(db));
- // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
- // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
- // the user.
- if r.contains("not-supported>") {
- return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
- "rendering complex constants".to_string(),
- )));
- }
return Ok(r);
}
}
@@ -1990,11 +2166,7 @@ impl Static {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type {
- let data = db.static_data(self.id);
- let resolver = self.id.resolver(db.upcast());
- let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
- let ty = ctx.lower_ty(&data.type_ref);
- Type::new_with_resolver_inner(db, &resolver, ty)
+ Type::from_value_def(db, self.id)
}
}
@@ -2366,7 +2538,7 @@ impl AsAssocItem for DefWithBody {
match self {
DefWithBody::Function(it) => it.as_assoc_item(db),
DefWithBody::Const(it) => it.as_assoc_item(db),
- DefWithBody::Static(_) | DefWithBody::Variant(_) => None,
+ DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None,
}
}
}
@@ -2492,14 +2664,22 @@ impl GenericDef {
Either::Right(x) => GenericParam::TypeParam(x),
}
});
- let lt_params = generics
+ self.lifetime_params(db)
+ .into_iter()
+ .map(GenericParam::LifetimeParam)
+ .chain(ty_params)
+ .collect()
+ }
+
+ pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> {
+ let generics = db.generic_params(self.into());
+ generics
.lifetimes
.iter()
.map(|(local_id, _)| LifetimeParam {
id: LifetimeParamId { parent: self.into(), local_id },
})
- .map(GenericParam::LifetimeParam);
- lt_params.chain(ty_params).collect()
+ .collect()
}
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
@@ -2545,8 +2725,12 @@ impl LocalSource {
self.source.file_id.original_file(db.upcast())
}
- pub fn name(&self) -> Option<ast::Name> {
- self.source.value.name()
+ pub fn file(&self) -> HirFileId {
+ self.source.file_id
+ }
+
+ pub fn name(&self) -> Option<InFile<ast::Name>> {
+ self.source.as_ref().map(|it| it.name()).transpose()
}
pub fn syntax(&self) -> &SyntaxNode {
@@ -2616,6 +2800,22 @@ impl Local {
/// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
let (body, source_map) = db.body_with_source_map(self.parent);
+ self.sources_(db, &body, &source_map).collect()
+ }
+
+ /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
+ pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
+ let (body, source_map) = db.body_with_source_map(self.parent);
+ let src = self.sources_(db, &body, &source_map).next().unwrap();
+ src
+ }
+
+ fn sources_<'a>(
+ self,
+ db: &'a dyn HirDatabase,
+ body: &'a hir_def::body::Body,
+ source_map: &'a hir_def::body::BodySourceMap,
+ ) -> impl Iterator<Item = LocalSource> + 'a {
body[self.binding_id]
.definitions
.iter()
@@ -2628,14 +2828,7 @@ impl Local {
Either::Right(it) => Either::Right(it.to_node(&root)),
})
})
- .map(|source| LocalSource { local: self, source })
- .collect()
- }
-
- /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
- pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
- let all_sources = self.sources(db);
- all_sources.into_iter().next().unwrap()
+ .map(move |source| LocalSource { local: self, source })
}
}
@@ -2689,9 +2882,7 @@ impl BuiltinAttr {
}
fn builtin(name: &str) -> Option<Self> {
- hir_def::builtin_attr::INERT_ATTRIBUTES
- .iter()
- .position(|tool| tool.name == name)
+ hir_def::attr::builtin::find_builtin_attr_idx(name)
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
}
@@ -2699,14 +2890,14 @@ impl BuiltinAttr {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
- None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
+ None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
}
}
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
match self.krate {
Some(_) => None,
- None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
+ None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
}
}
}
@@ -2729,7 +2920,7 @@ impl ToolModule {
}
fn builtin(name: &str) -> Option<Self> {
- hir_def::builtin_attr::TOOL_MODULES
+ hir_def::attr::builtin::TOOL_MODULES
.iter()
.position(|&tool| tool == name)
.map(|idx| ToolModule { krate: None, idx: idx as u32 })
@@ -2739,7 +2930,7 @@ impl ToolModule {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
- None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
+ None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
}
}
}
@@ -3117,6 +3308,103 @@ impl TraitRef {
}
}
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Closure {
+ id: ClosureId,
+ subst: Substitution,
+}
+
+impl From<Closure> for ClosureId {
+ fn from(value: Closure) -> Self {
+ value.id
+ }
+}
+
+impl Closure {
+ fn as_ty(self) -> Ty {
+ TyKind::Closure(self.id, self.subst).intern(Interner)
+ }
+
+ pub fn display_with_id(&self, db: &dyn HirDatabase) -> String {
+ self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
+ }
+
+ pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String {
+ self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string()
+ }
+
+ pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec<ClosureCapture> {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let info = infer.closure_info(&self.id);
+ info.0
+ .iter()
+ .cloned()
+ .map(|capture| ClosureCapture { owner, closure: self.id, capture })
+ .collect()
+ }
+
+ pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec<Type> {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let (captures, _) = infer.closure_info(&self.id);
+ captures
+ .iter()
+ .cloned()
+ .map(|capture| Type {
+ env: db.trait_environment_for_body(owner),
+ ty: capture.ty(&self.subst),
+ })
+ .collect()
+ }
+
+ pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let info = infer.closure_info(&self.id);
+ info.1
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ClosureCapture {
+ owner: DefWithBodyId,
+ closure: ClosureId,
+ capture: hir_ty::CapturedItem,
+}
+
+impl ClosureCapture {
+ pub fn local(&self) -> Local {
+ Local { parent: self.owner, binding_id: self.capture.local() }
+ }
+
+ pub fn kind(&self) -> CaptureKind {
+ match self.capture.kind() {
+ hir_ty::CaptureKind::ByRef(
+ hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared,
+ ) => CaptureKind::SharedRef,
+ hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Unique) => {
+ CaptureKind::UniqueSharedRef
+ }
+ hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut { .. }) => {
+ CaptureKind::MutableRef
+ }
+ hir_ty::CaptureKind::ByValue => CaptureKind::Move,
+ }
+ }
+
+ pub fn display_place(&self, db: &dyn HirDatabase) -> String {
+ self.capture.display_place(self.owner, db)
+ }
+}
+
+pub enum CaptureKind {
+ SharedRef,
+ UniqueSharedRef,
+ MutableRef,
+ Move,
+}
+
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Type {
env: Arc<TraitEnvironment>,
@@ -3164,24 +3452,33 @@ impl Type {
Type { env: environment, ty }
}
- fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type {
- let ty_def = def.into();
- let parent_subst = match ty_def {
- TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container {
- ItemContainerId::TraitId(id) => {
- let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
- Some(subst)
- }
- ItemContainerId::ImplId(id) => {
- let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
- Some(subst)
- }
- _ => None,
+ fn from_def(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type {
+ let ty = db.ty(def.into());
+ let substs = TyBuilder::unknown_subst(
+ db,
+ match def.into() {
+ TyDefId::AdtId(it) => GenericDefId::AdtId(it),
+ TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
+ TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()),
},
- _ => None,
- };
- let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build();
- Type::new(db, def, ty)
+ );
+ Type::new(db, def, ty.substitute(Interner, &substs))
+ }
+
+ fn from_value_def(db: &dyn HirDatabase, def: impl Into<ValueTyDefId> + HasResolver) -> Type {
+ let ty = db.value_ty(def.into());
+ let substs = TyBuilder::unknown_subst(
+ db,
+ match def.into() {
+ ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it),
+ ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it),
+ ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)),
+ ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)),
+ ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it),
+ ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()),
+ },
+ );
+ Type::new(db, def, ty.substitute(Interner, &substs))
}
pub fn new_slice(ty: Type) -> Type {
@@ -3237,6 +3534,14 @@ impl Type {
}
}
+ pub fn is_scalar(&self) -> bool {
+ matches!(self.ty.kind(Interner), TyKind::Scalar(_))
+ }
+
+ pub fn is_tuple(&self) -> bool {
+ matches!(self.ty.kind(Interner), TyKind::Tuple(..))
+ }
+
pub fn remove_ref(&self) -> Option<Type> {
match &self.ty.kind(Interner) {
TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
@@ -3331,7 +3636,7 @@ impl Type {
binders: CanonicalVarKinds::empty(Interner),
};
- db.trait_solve(self.env.krate, goal).is_some()
+ db.trait_solve(self.env.krate, self.env.block, goal).is_some()
}
pub fn normalize_trait_assoc_type(
@@ -3378,7 +3683,12 @@ impl Type {
}
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
+ let mut the_ty = &self.ty;
let callee = match self.ty.kind(Interner) {
+ TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
+ the_ty = ty;
+ Callee::Closure(ty.as_closure().unwrap())
+ }
TyKind::Closure(id, _) => Callee::Closure(*id),
TyKind::Function(_) => Callee::FnPtr,
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
@@ -3393,7 +3703,7 @@ impl Type {
}
};
- let sig = self.ty.callable_sig(db)?;
+ let sig = the_ty.callable_sig(db)?;
Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
}
@@ -3401,6 +3711,13 @@ impl Type {
matches!(self.ty.kind(Interner), TyKind::Closure { .. })
}
+ pub fn as_closure(&self) -> Option<Closure> {
+ match self.ty.kind(Interner) {
+ TyKind::Closure(id, subst) => Some(Closure { id: *id, subst: subst.clone() }),
+ _ => None,
+ }
+ }
+
pub fn is_fn(&self) -> bool {
matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
}
@@ -3502,23 +3819,24 @@ impl Type {
}
}
- pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> {
+ pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> {
if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
- try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize))
+ try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize))
} else {
None
}
}
- pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
+ /// Returns types that this type dereferences to (including this type itself). The returned
+ /// iterator won't yield the same type more than once even if the deref chain contains a cycle.
+ pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {
self.autoderef_(db).map(move |ty| self.derived(ty))
}
- fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
+ fn autoderef_(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Ty> {
// There should be no inference vars in types passed here
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
- let environment = self.env.clone();
- autoderef(db, environment, canonical).map(|canonical| canonical.value)
+ autoderef(db, self.env.clone(), canonical)
}
// This would be nicer if it just returned an iterator, but that runs into
@@ -3636,7 +3954,7 @@ impl Type {
self.as_adt()
.and_then(|a| a.lifetime(db).and_then(|lt| Some((&lt.name).to_smol_str())))
.into_iter()
- // add the type and const paramaters
+ // add the type and const parameters
.chain(self.type_and_const_arguments(db))
}
@@ -3955,6 +4273,11 @@ impl Type {
.map(|id| TypeOrConstParam { id }.split(db).either_into())
.collect()
}
+
+ pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ db.layout_of_ty(self.ty.clone(), self.env.krate)
+ .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap()))
+ }
}
// FIXME: Document this
@@ -4064,6 +4387,48 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::Closu
}
}
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>);
+
+impl Layout {
+ pub fn size(&self) -> u64 {
+ self.0.size.bytes()
+ }
+
+ pub fn align(&self) -> u64 {
+ self.0.align.abi.bytes()
+ }
+
+ pub fn niches(&self) -> Option<u128> {
+ Some(self.0.largest_niche?.available(&*self.1))
+ }
+
+ pub fn field_offset(&self, idx: usize) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => Some(0),
+ layout::FieldsShape::Array { stride, count } => {
+ let i = u64::try_from(idx).ok()?;
+ (i < count).then_some((stride * i).bytes())
+ }
+ layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()),
+ }
+ }
+
+ pub fn enum_tag_size(&self) -> Option<usize> {
+ let tag_size =
+ if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants {
+ match tag_encoding {
+ TagEncoding::Direct => tag.size(&*self.1).bytes_usize(),
+ TagEncoding::Niche { .. } => 0,
+ }
+ } else {
+ return None;
+ };
+ Some(tag_size)
+ }
+}
+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BindingMode {
Move,
@@ -4215,6 +4580,12 @@ impl HasCrate for Union {
}
}
+impl HasCrate for Enum {
+ fn krate(&self, db: &dyn HirDatabase) -> Crate {
+ self.module(db).krate()
+ }
+}
+
impl HasCrate for Field {
fn krate(&self, db: &dyn HirDatabase) -> Crate {
self.parent_def(db).module(db).krate()
@@ -4286,3 +4657,90 @@ impl HasCrate for Module {
Module::krate(*self)
}
}
+
+pub trait HasContainer {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer;
+}
+
+impl HasContainer for Module {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ // FIXME: handle block expressions as modules (their parent is in a different DefMap)
+ let def_map = self.id.def_map(db.upcast());
+ match def_map[self.id.local_id].parent {
+ Some(parent_id) => ItemContainer::Module(Module { id: def_map.module_id(parent_id) }),
+ None => ItemContainer::Crate(def_map.krate()),
+ }
+ }
+}
+
+impl HasContainer for Function {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Struct {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for Union {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for Enum {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for TypeAlias {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Const {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Static {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Trait {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for TraitAlias {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+fn container_id_to_hir(c: ItemContainerId) -> ItemContainer {
+ match c {
+ ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(),
+ ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }),
+ ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }),
+ ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }),
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ItemContainer {
+ Trait(Trait),
+ Impl(Impl),
+ Module(Module),
+ ExternBlock(),
+ Crate(CrateId),
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 407ba6f65..5a76a9185 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -7,9 +7,10 @@ use std::{cell::RefCell, fmt, iter, mem, ops};
use base_db::{FileId, FileRange};
use either::Either;
use hir_def::{
- body,
- expr::Expr,
+ hir::Expr,
+ lower::LowerCtx,
macro_id_to_def_id,
+ nameres::MacroSubNs,
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
@@ -140,7 +141,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.parse(file_id)
}
- pub fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode> {
+ pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
self.imp.parse_or_expand(file_id)
}
@@ -350,6 +351,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.type_of_pat(pat)
}
+ /// It also includes the changes that binding mode makes in the type. For example in
+ /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
+ /// of this function is `&mut Option<T>`
+ pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ self.imp.type_of_binding_in_pat(pat)
+ }
+
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.imp.type_of_self(param)
}
@@ -475,10 +483,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.scope_at_offset(node, offset)
}
- pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
- self.imp.scope_for_def(def)
- }
-
pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.imp.assert_contains_node(node)
}
@@ -518,23 +522,23 @@ impl<'db> SemanticsImpl<'db> {
tree
}
- fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode> {
- let node = self.db.parse_or_expand(file_id)?;
+ fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
+ let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
- Some(node)
+ node
}
fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
let sa = self.analyze_no_infer(macro_call.syntax())?;
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
- let node = self.parse_or_expand(file_id)?;
+ let node = self.parse_or_expand(file_id);
Some(node)
}
fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
- self.parse_or_expand(macro_call_id.as_file())
+ Some(self.parse_or_expand(macro_call_id.as_file()))
}
fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
@@ -543,7 +547,7 @@ impl<'db> SemanticsImpl<'db> {
let call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
})?;
- self.parse_or_expand(call_id.as_file())
+ Some(self.parse_or_expand(call_id.as_file()))
}
fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
@@ -566,7 +570,7 @@ impl<'db> SemanticsImpl<'db> {
.into_iter()
.flat_map(|call| {
let file_id = call?.as_file();
- let node = self.db.parse_or_expand(file_id)?;
+ let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
Some(node)
})
@@ -609,7 +613,7 @@ impl<'db> SemanticsImpl<'db> {
let krate = resolver.krate();
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
resolver
- .resolve_path_as_macro(self.db.upcast(), &path)
+ .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
.map(|it| macro_id_to_def_id(self.db.upcast(), it))
})?;
hir_expand::db::expand_speculative(
@@ -990,7 +994,7 @@ impl<'db> SemanticsImpl<'db> {
}
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
- let root = self.parse_or_expand(src.file_id).unwrap();
+ let root = self.parse_or_expand(src.file_id);
let node = src.map(|it| it.to_node(&root));
node.as_ref().original_file_range(self.db.upcast())
}
@@ -1065,21 +1069,22 @@ impl<'db> SemanticsImpl<'db> {
fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
- let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id);
- let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
- .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
+ let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
+ let ty = hir_ty::TyLoweringContext::new(
+ self.db,
+ &analyze.resolver,
+ analyze.resolver.module().into(),
+ )
+ .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
}
fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
let analyze = self.analyze(path.syntax())?;
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
- let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
+ let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
- match analyze
- .resolver
- .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
- {
+ match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
TypeNs::TraitId(id) => Some(Trait { id }),
_ => None,
}
@@ -1141,6 +1146,10 @@ impl<'db> SemanticsImpl<'db> {
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
+ fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
+ }
+
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.analyze(param.syntax())?.type_of_self(self.db, param)
}
@@ -1298,12 +1307,6 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
- let file_id = self.db.lookup_intern_trait(def.id).id.file_id();
- let resolver = def.id.resolver(self.db.upcast());
- SemanticsScope { db: self.db, file_id, resolver }
- }
-
fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
@@ -1647,6 +1650,7 @@ impl<'a> SemanticsScope<'a> {
VisibleTraits(resolver.traits_in_scope(self.db.upcast()))
}
+ /// Calls the passed closure `f` on all names in scope.
pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let scope = self.resolver.names_in_scope(self.db.upcast());
for (name, entries) in scope {
@@ -1674,7 +1678,7 @@ impl<'a> SemanticsScope<'a> {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
- let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
+ let ctx = LowerCtx::with_file_id(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index f6f8c9a25..c50ffa4f8 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -14,7 +14,7 @@
//! expression, an item definition.
//!
//! Knowing only the syntax gives us relatively little info. For example,
-//! looking at the syntax of the function we can realise that it is a part of an
+//! looking at the syntax of the function we can realize that it is a part of an
//! `impl` block, but we won't be able to tell what trait function the current
//! function overrides, and whether it does that correctly. For that, we need to
//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
@@ -88,9 +88,11 @@
use base_db::FileId;
use hir_def::{
child_by_source::ChildBySource,
- dyn_map::DynMap,
- expr::{BindingId, LabelId},
- keys::{self, Key},
+ dyn_map::{
+ keys::{self, Key},
+ DynMap,
+ },
+ hir::{BindingId, LabelId},
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index c24d196e1..ecb1b306a 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -5,21 +5,19 @@
//!
//! So, this modules should not be used during hir construction, it exists
//! purely for "IDE needs".
-use std::{
- iter::{self, once},
- sync::Arc,
-};
+use std::iter::{self, once};
use either::Either;
use hir_def::{
body::{
- self,
scope::{ExprScopes, ScopeId},
Body, BodySourceMap,
},
- expr::{ExprId, Pat, PatId},
+ hir::{BindingId, ExprId, Pat, PatId},
lang_item::LangItem,
+ lower::LowerCtx,
macro_id_to_def_id,
+ nameres::MacroSubNs,
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
@@ -39,8 +37,9 @@ use hir_ty::{
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
UnsafeExpr,
},
- method_resolution::{self, lang_items_for_bin_op},
- Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
+ lang_items::lang_items_for_bin_op,
+ method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
+ TyLoweringContext,
};
use itertools::Itertools;
use smallvec::SmallVec;
@@ -48,6 +47,7 @@ use syntax::{
ast::{self, AstNode},
SyntaxKind, SyntaxNode, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
@@ -134,13 +134,22 @@ impl SourceAnalyzer {
self.body_source_map()?.node_pat(src)
}
+ fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
+ let pat_id = self.pat_id(&pat.clone().into())?;
+ if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] {
+ Some(id)
+ } else {
+ None
+ }
+ }
+
fn expand_expr(
&self,
db: &dyn HirDatabase,
expr: InFile<ast::MacroCall>,
) -> Option<InFile<ast::Expr>> {
let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?;
- let expanded = db.parse_or_expand(macro_file)?;
+ let expanded = db.parse_or_expand(macro_file);
let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) {
match stmts.expr()? {
ast::Expr::MacroExpr(mac) => {
@@ -199,6 +208,18 @@ impl SourceAnalyzer {
Some((mk_ty(ty), coerced.map(mk_ty)))
}
+ pub(crate) fn type_of_binding_in_pat(
+ &self,
+ db: &dyn HirDatabase,
+ pat: &ast::IdentPat,
+ ) -> Option<Type> {
+ let binding_id = self.binding_id_of_pat(pat)?;
+ let infer = self.infer.as_ref()?;
+ let ty = infer[binding_id].clone();
+ let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
+ Some(mk_ty(ty))
+ }
+
pub(crate) fn type_of_self(
&self,
db: &dyn HirDatabase,
@@ -215,9 +236,9 @@ impl SourceAnalyzer {
_db: &dyn HirDatabase,
pat: &ast::IdentPat,
) -> Option<BindingMode> {
- let pat_id = self.pat_id(&pat.clone().into())?;
+ let binding_id = self.binding_id_of_pat(pat)?;
let infer = self.infer.as_ref()?;
- infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
+ infer.binding_modes.get(binding_id).map(|bm| match bm {
hir_ty::BindingMode::Move => BindingMode::Move,
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
@@ -420,7 +441,10 @@ impl SourceAnalyzer {
None
} else {
// Shorthand syntax, resolve to the local
- let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
+ let path = Path::from_known_path_with_no_generic(ModPath::from_segments(
+ PathKind::Plain,
+ once(local_name.clone()),
+ ));
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
Some(ValueNs::LocalBinding(binding_id)) => {
Some(Local { binding_id, parent: self.resolver.body_owner()? })
@@ -459,9 +483,11 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>,
) -> Option<Macro> {
- let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
+ let ctx = LowerCtx::with_file_id(db.upcast(), macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
- self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
+ self.resolver
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
+ .map(|it| it.into())
}
pub(crate) fn resolve_bind_pat_to_const(
@@ -571,7 +597,7 @@ impl SourceAnalyzer {
// This must be a normal source file rather than macro file.
let hygiene = Hygiene::new(db.upcast(), self.file_id);
- let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene);
+ let ctx = LowerCtx::with_hygiene(db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
@@ -655,7 +681,7 @@ impl SourceAnalyzer {
}
}
}
- return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
+ return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) {
Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),
// this labels any path that starts with a tool module as the tool itself, this is technically wrong
// but there is no benefit in differentiating these two cases for the time being
@@ -733,7 +759,7 @@ impl SourceAnalyzer {
let krate = self.resolver.krate();
let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
self.resolver
- .resolve_path_as_macro(db.upcast(), &path)
+ .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang))
.map(|it| macro_id_to_def_id(db.upcast(), it))
})?;
Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
@@ -801,15 +827,11 @@ impl SourceAnalyzer {
func: FunctionId,
substs: Substitution,
) -> FunctionId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return func,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_method(db, env, func, substs).0
}
@@ -819,15 +841,11 @@ impl SourceAnalyzer {
const_id: ConstId,
subs: Substitution,
) -> ConstId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return const_id,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_const(db, env, const_id, subs).0
}
@@ -941,12 +959,14 @@ pub(crate) fn resolve_hir_path(
}
#[inline]
-pub(crate) fn resolve_hir_path_as_macro(
+pub(crate) fn resolve_hir_path_as_attr_macro(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &Path,
) -> Option<Macro> {
- resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into)
+ resolver
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr))
+ .map(Into::into)
}
fn resolve_hir_path_(
@@ -958,12 +978,12 @@ fn resolve_hir_path_(
let types = || {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => {
- let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
+ let (_, res) = TyLoweringContext::new(db, resolver, resolver.module().into())
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
- let (ty, remaining_idx) =
- resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
+ let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1019,7 +1039,7 @@ fn resolve_hir_path_(
let body_owner = resolver.body_owner();
let values = || {
- resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
+ resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(binding_id) => {
let var = Local { parent: body_owner?, binding_id };
@@ -1039,14 +1059,14 @@ fn resolve_hir_path_(
let items = || {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
};
let macros = || {
resolver
- .resolve_path_as_macro(db.upcast(), path.mod_path())
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, None)
.map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
};
@@ -1074,7 +1094,7 @@ fn resolve_hir_path_qualifier(
path: &Path,
) -> Option<PathResolution> {
resolver
- .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
+ .resolve_path_in_type_ns_fully(db.upcast(), &path)
.map(|ty| match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
@@ -1089,7 +1109,7 @@ fn resolve_hir_path_qualifier(
})
.or_else(|| {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
})
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index a9afa1c6f..43d957412 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -2,23 +2,25 @@
use base_db::FileRange;
use hir_def::{
- item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId,
- HasModule, ImplId, ItemContainerId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId,
+ src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId,
+ ModuleDefId, ModuleId, TraitId,
};
use hir_expand::{HirFileId, InFile};
use hir_ty::db::HirDatabase;
use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr};
-use crate::{Module, Semantics};
+use crate::{Module, ModuleDef, Semantics};
/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
+ // even though name can be derived from the def, we store it for efficiency
pub name: SmolStr,
+ pub def: ModuleDef,
pub loc: DeclarationLocation,
- pub kind: FileSymbolKind,
pub container_name: Option<SmolStr>,
+ pub is_alias: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -32,18 +34,26 @@ pub struct DeclarationLocation {
}
impl DeclarationLocation {
- pub fn syntax<DB: HirDatabase>(&self, sema: &Semantics<'_, DB>) -> Option<SyntaxNode> {
- let root = sema.parse_or_expand(self.hir_file_id)?;
- Some(self.ptr.to_node(&root))
+ pub fn syntax<DB: HirDatabase>(&self, sema: &Semantics<'_, DB>) -> SyntaxNode {
+ let root = sema.parse_or_expand(self.hir_file_id);
+ self.ptr.to_node(&root)
}
- pub fn original_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
- let node = resolve_node(db, self.hir_file_id, &self.ptr)?;
- Some(node.as_ref().original_file_range(db.upcast()))
+ pub fn original_range(&self, db: &dyn HirDatabase) -> FileRange {
+ if let Some(file_id) = self.hir_file_id.file_id() {
+ // fast path to prevent parsing
+ return FileRange { file_id, range: self.ptr.text_range() };
+ }
+ let node = resolve_node(db, self.hir_file_id, &self.ptr);
+ node.as_ref().original_file_range(db.upcast())
}
pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
- let node = resolve_node(db, self.hir_file_id, &self.name_ptr)?;
+ if let Some(file_id) = self.hir_file_id.file_id() {
+ // fast path to prevent parsing
+ return Some(FileRange { file_id, range: self.name_ptr.text_range() });
+ }
+ let node = resolve_node(db, self.hir_file_id, &self.name_ptr);
node.as_ref().original_file_range_opt(db.upcast())
}
}
@@ -52,38 +62,10 @@ fn resolve_node(
db: &dyn HirDatabase,
file_id: HirFileId,
ptr: &SyntaxNodePtr,
-) -> Option<InFile<SyntaxNode>> {
- let root = db.parse_or_expand(file_id)?;
+) -> InFile<SyntaxNode> {
+ let root = db.parse_or_expand(file_id);
let node = ptr.to_node(&root);
- Some(InFile::new(file_id, node))
-}
-
-#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
-pub enum FileSymbolKind {
- Const,
- Enum,
- Function,
- Macro,
- Module,
- Static,
- Struct,
- Trait,
- TraitAlias,
- TypeAlias,
- Union,
-}
-
-impl FileSymbolKind {
- pub fn is_type(self: FileSymbolKind) -> bool {
- matches!(
- self,
- FileSymbolKind::Struct
- | FileSymbolKind::Enum
- | FileSymbolKind::Trait
- | FileSymbolKind::TypeAlias
- | FileSymbolKind::Union
- )
- }
+ InFile::new(file_id, node)
}
/// Represents an outstanding module that the symbol collector must collect symbols from.
@@ -102,21 +84,33 @@ pub struct SymbolCollector<'a> {
/// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
/// all symbols that should be indexed for the given module.
impl<'a> SymbolCollector<'a> {
- pub fn collect(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
- let mut symbol_collector = SymbolCollector {
+ pub fn new(db: &'a dyn HirDatabase) -> Self {
+ SymbolCollector {
db,
symbols: Default::default(),
+ work: Default::default(),
current_container_name: None,
- // The initial work is the root module we're collecting, additional work will
- // be populated as we traverse the module's definitions.
- work: vec![SymbolCollectorWork { module_id: module.into(), parent: None }],
- };
+ }
+ }
+
+ pub fn collect(&mut self, module: Module) {
+ // The initial work is the root module we're collecting, additional work will
+ // be populated as we traverse the module's definitions.
+ self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
- while let Some(work) = symbol_collector.work.pop() {
- symbol_collector.do_work(work);
+ while let Some(work) = self.work.pop() {
+ self.do_work(work);
}
+ }
+
+ pub fn finish(self) -> Vec<FileSymbol> {
+ self.symbols
+ }
- symbol_collector.symbols
+ pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
+ let mut symbol_collector = SymbolCollector::new(db);
+ symbol_collector.collect(module);
+ symbol_collector.finish()
}
fn do_work(&mut self, work: SymbolCollectorWork) {
@@ -134,36 +128,34 @@ impl<'a> SymbolCollector<'a> {
match module_def_id {
ModuleDefId::ModuleId(id) => self.push_module(id),
ModuleDefId::FunctionId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Function);
+ self.push_decl(id);
self.collect_from_body(id);
}
- ModuleDefId::AdtId(AdtId::StructId(id)) => {
- self.push_decl(id, FileSymbolKind::Struct)
- }
- ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, FileSymbolKind::Enum),
- ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, FileSymbolKind::Union),
+ ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id),
+ ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id),
+ ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id),
ModuleDefId::ConstId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Const);
+ self.push_decl(id);
self.collect_from_body(id);
}
ModuleDefId::StaticId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Static);
+ self.push_decl(id);
self.collect_from_body(id);
}
ModuleDefId::TraitId(id) => {
- self.push_decl(id, FileSymbolKind::Trait);
+ self.push_decl(id);
self.collect_from_trait(id);
}
ModuleDefId::TraitAliasId(id) => {
- self.push_decl(id, FileSymbolKind::TraitAlias);
+ self.push_decl(id);
}
ModuleDefId::TypeAliasId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::TypeAlias);
+ self.push_decl(id);
}
ModuleDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro),
+ MacroId::Macro2Id(id) => self.push_decl(id),
+ MacroId::MacroRulesId(id) => self.push_decl(id),
+ MacroId::ProcMacroId(id) => self.push_decl(id),
},
// Don't index these.
ModuleDefId::BuiltinType(_) => {}
@@ -183,9 +175,9 @@ impl<'a> SymbolCollector<'a> {
for &id in id {
if id.module(self.db.upcast()) == module_id {
match id {
- MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro),
+ MacroId::Macro2Id(id) => self.push_decl(id),
+ MacroId::MacroRulesId(id) => self.push_decl(id),
+ MacroId::ProcMacroId(id) => self.push_decl(id),
}
}
}
@@ -233,124 +225,95 @@ impl<'a> SymbolCollector<'a> {
}
}
- fn current_container_name(&self) -> Option<SmolStr> {
- self.current_container_name.clone()
- }
-
fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
match body_id {
- DefWithBodyId::FunctionId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::StaticId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::ConstId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::VariantId(id) => Some({
- let db = self.db.upcast();
- id.parent.lookup(db).source(db).value.name()?.text().into()
- }),
+ DefWithBodyId::FunctionId(id) => Some(self.db.function_data(id).name.to_smol_str()),
+ DefWithBodyId::StaticId(id) => Some(self.db.static_data(id).name.to_smol_str()),
+ DefWithBodyId::ConstId(id) => Some(self.db.const_data(id).name.as_ref()?.to_smol_str()),
+ DefWithBodyId::VariantId(id) => {
+ Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str())
+ }
+ DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
}
}
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
match assoc_item_id {
- AssocItemId::FunctionId(id) => self.push_decl_assoc(id, FileSymbolKind::Function),
- AssocItemId::ConstId(id) => self.push_decl_assoc(id, FileSymbolKind::Const),
- AssocItemId::TypeAliasId(id) => self.push_decl_assoc(id, FileSymbolKind::TypeAlias),
+ AssocItemId::FunctionId(id) => self.push_decl(id),
+ AssocItemId::ConstId(id) => self.push_decl(id),
+ AssocItemId::TypeAliasId(id) => self.push_decl(id),
}
}
- fn push_decl_assoc<L, T>(&mut self, id: L, kind: FileSymbolKind)
+ fn push_decl<L>(&mut self, id: L)
where
- L: Lookup<Data = AssocItemLoc<T>>,
- T: ItemTreeNode,
- <T as ItemTreeNode>::Source: HasName,
+ L: Lookup + Into<ModuleDefId>,
+ <L as Lookup>::Data: HasSource,
+ <<L as Lookup>::Data as HasSource>::Value: HasName,
{
- fn container_name(db: &dyn HirDatabase, container: ItemContainerId) -> Option<SmolStr> {
- match container {
- ItemContainerId::ModuleId(module_id) => {
- let module = Module::from(module_id);
- module.name(db).and_then(|name| name.as_text())
- }
- ItemContainerId::TraitId(trait_id) => {
- let trait_data = db.trait_data(trait_id);
- trait_data.name.as_text()
- }
- ItemContainerId::ImplId(_) | ItemContainerId::ExternBlockId(_) => None,
+ let loc = id.lookup(self.db.upcast());
+ let source = loc.source(self.db.upcast());
+ let Some(name_node) = source.value.name() else { return };
+ let def = ModuleDef::from(id.into());
+ let dec_loc = DeclarationLocation {
+ hir_file_id: source.file_id,
+ ptr: SyntaxNodePtr::new(source.value.syntax()),
+ name_ptr: SyntaxNodePtr::new(name_node.syntax()),
+ };
+
+ if let Some(attrs) = def.attrs(self.db) {
+ for alias in attrs.doc_aliases() {
+ self.symbols.push(FileSymbol {
+ name: alias,
+ def,
+ loc: dec_loc.clone(),
+ container_name: self.current_container_name.clone(),
+ is_alias: true,
+ });
}
}
- self.push_file_symbol(|s| {
- let loc = id.lookup(s.db.upcast());
- let source = loc.source(s.db.upcast());
- let name_node = source.value.name()?;
- let container_name =
- container_name(s.db, loc.container).or_else(|| s.current_container_name());
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind,
- container_name,
- loc: DeclarationLocation {
- hir_file_id: source.file_id,
- ptr: SyntaxNodePtr::new(source.value.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
- }
-
- fn push_decl<L>(&mut self, id: L, kind: FileSymbolKind)
- where
- L: Lookup,
- <L as Lookup>::Data: HasSource,
- <<L as Lookup>::Data as HasSource>::Value: HasName,
- {
- self.push_file_symbol(|s| {
- let loc = id.lookup(s.db.upcast());
- let source = loc.source(s.db.upcast());
- let name_node = source.value.name()?;
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind,
- container_name: s.current_container_name(),
- loc: DeclarationLocation {
- hir_file_id: source.file_id,
- ptr: SyntaxNodePtr::new(source.value.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
+ self.symbols.push(FileSymbol {
+ name: name_node.text().into(),
+ def,
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
}
fn push_module(&mut self, module_id: ModuleId) {
- self.push_file_symbol(|s| {
- let def_map = module_id.def_map(s.db.upcast());
- let module_data = &def_map[module_id.local_id];
- let declaration = module_data.origin.declaration()?;
- let module = declaration.to_node(s.db.upcast());
- let name_node = module.name()?;
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind: FileSymbolKind::Module,
- container_name: s.current_container_name(),
- loc: DeclarationLocation {
- hir_file_id: declaration.file_id,
- ptr: SyntaxNodePtr::new(module.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
- }
+ let def_map = module_id.def_map(self.db.upcast());
+ let module_data = &def_map[module_id.local_id];
+ let Some(declaration) = module_data.origin.declaration() else { return };
+ let module = declaration.to_node(self.db.upcast());
+ let Some(name_node) = module.name() else { return };
+ let dec_loc = DeclarationLocation {
+ hir_file_id: declaration.file_id,
+ ptr: SyntaxNodePtr::new(module.syntax()),
+ name_ptr: SyntaxNodePtr::new(name_node.syntax()),
+ };
- fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option<FileSymbol>) {
- if let Some(file_symbol) = f(self) {
- self.symbols.push(file_symbol);
+ let def = ModuleDef::Module(module_id.into());
+
+ if let Some(attrs) = def.attrs(self.db) {
+ for alias in attrs.doc_aliases() {
+ self.symbols.push(FileSymbol {
+ name: alias,
+ def,
+ loc: dec_loc.clone(),
+ container_name: self.current_container_name.clone(),
+ is_alias: true,
+ });
+ }
}
+
+ self.symbols.push(FileSymbol {
+ name: name_node.text().into(),
+ def: ModuleDef::Module(module_id.into()),
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
index 785ae3d09..8bc285614 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
return None;
}
- let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?;
acc.add(
AssistId("add_explicit_type", AssistKind::RefactorRewrite),
format!("Insert explicit type `{inferred_type}`"),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 4e11b31de..d07c63726 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -1,13 +1,9 @@
use hir::HasSource;
-use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into;
use syntax::ast::{self, make, AstNode};
use crate::{
assist_context::{AssistContext, Assists},
- utils::{
- add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, render_snippet,
- Cursor, DefaultMethods,
- },
+ utils::{add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, DefaultMethods},
AssistId, AssistKind,
};
@@ -130,50 +126,36 @@ fn add_missing_impl_members_inner(
}
let target = impl_def.syntax().text_range();
- acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
- let missing_items = missing_items
- .into_iter()
- .map(|it| {
- if ctx.sema.hir_file_for(it.syntax()).is_macro() {
- if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
- return it;
- }
- }
- it.clone_for_update()
- })
- .collect();
- let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
+ acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |edit| {
+ let new_impl_def = edit.make_mut(impl_def.clone());
+ let first_new_item = add_trait_assoc_items_to_impl(
&ctx.sema,
- missing_items,
+ &missing_items,
trait_,
- impl_def.clone(),
+ &new_impl_def,
target_scope,
);
- match ctx.config.snippet_cap {
- None => builder.replace(target, new_impl_def.to_string()),
- Some(cap) => {
- let mut cursor = Cursor::Before(first_new_item.syntax());
- let placeholder;
- if let DefaultMethods::No = mode {
- if let ast::AssocItem::Fn(func) = &first_new_item {
- if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
- if let Some(m) =
- func.syntax().descendants().find_map(ast::MacroCall::cast)
- {
- if m.syntax().text() == "todo!()" {
- placeholder = m;
- cursor = Cursor::Replace(placeholder.syntax());
- }
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ let mut placeholder = None;
+ if let DefaultMethods::No = mode {
+ if let ast::AssocItem::Fn(func) = &first_new_item {
+ if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
+ if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
+ {
+ if m.syntax().text() == "todo!()" {
+ placeholder = Some(m);
}
}
}
}
- builder.replace_snippet(
- cap,
- target,
- render_snippet(cap, new_impl_def.syntax(), cursor),
- )
}
+
+ if let Some(macro_call) = placeholder {
+ edit.add_placeholder_snippet(cap, macro_call);
+ } else {
+ edit.add_tabstop_before(cap, first_new_item);
+ };
};
})
}
@@ -184,7 +166,8 @@ fn try_gen_trait_body(
trait_ref: hir::TraitRef,
impl_def: &ast::Impl,
) -> Option<()> {
- let trait_path = make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).to_string());
+ let trait_path =
+ make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db()).to_string());
let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?;
let adt = hir_ty.as_adt()?.source(ctx.db())?;
gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref))
@@ -252,7 +235,7 @@ impl Foo for S {
}
#[test]
- fn test_copied_overriden_members() {
+ fn test_copied_overridden_members() {
check_assist(
add_missing_impl_members,
r#"
@@ -365,6 +348,125 @@ impl<U> Foo<U> for S {
}
#[test]
+ fn test_lifetime_substitution() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+pub trait Trait<'a, 'b, A, B, C> {
+ fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#,
+ r#"
+pub trait Trait<'a, 'b, A, B, C> {
+ fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {
+ fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_lifetime_substitution_with_body() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+ fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+ let value: &'a i32 = &0;
+ (C::default(), value)
+ }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#,
+ r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+ fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+ let value: &'a i32 = &0;
+ (C::default(), value)
+ }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
+ $0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) {
+ let value: &'x i32 = &0;
+ (<U>::default(), value)
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_const_substitution() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+struct Bar<const: N: bool> {
+ bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+ fn get_n_sq(&self, arg: &T) -> usize { N * N }
+ fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+ wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+ $0
+}"#,
+ r#"
+struct Bar<const: N: bool> {
+ bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+ fn get_n_sq(&self, arg: &T) -> usize { N * N }
+ fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+ wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+ $0fn get_n_sq(&self, arg: &Z) -> usize { X * X }
+
+ fn get_array(&self, arg: Bar<X>) -> [i32; X] { [1; X] }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_const_substitution_2() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+trait Foo<const N: usize, const M: usize, T> {
+ fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+ $0
+}"#,
+ r#"
+trait Foo<const N: usize, const M: usize, T> {
+ fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+ $0fn get_sum(&self, arg: &X) -> usize { 42 + {20 + 22} }
+}"#,
+ )
+ }
+
+ #[test]
fn test_cursor_after_empty_impl_def() {
check_assist(
add_missing_impl_members,
@@ -746,6 +848,115 @@ impl Foo<T> for S<T> {
}
#[test]
+ fn test_qualify_generic_default_parameter() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct S;
+ pub trait Foo<T = S> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct S;
+ pub trait Foo<T = S> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::S) {
+ ${0:todo!()}
+ }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_qualify_generic_default_parameter_2() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = Wrapper<S, bool>> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = Wrapper<S, bool>> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::Wrapper<m::S, bool>) {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_qualify_generic_default_parameter_3() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = S, V = Wrapper<T, S>> {
+ fn bar(&self, other: &V);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = S, V = Wrapper<T, S>> {
+ fn bar(&self, other: &V);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::Wrapper<m::S, m::S>) {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn test_assoc_type_bounds_are_removed() {
check_assist(
add_missing_impl_members,
@@ -1346,8 +1557,8 @@ struct SomeStruct {
}
impl PartialEq for SomeStruct {
$0fn ne(&self, other: &Self) -> bool {
- !self.eq(other)
- }
+ !self.eq(other)
+ }
}
"#,
);
@@ -1511,8 +1722,245 @@ fn main() {
struct S;
impl Tr for S {
fn method() {
- ${0:todo!()}
+ ${0:todo!()}
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_add_missing_preserves_indentation() {
+ // in different modules
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub trait Foo {
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ }
+}
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub trait Foo {
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ }
+}
+struct S;
+impl m::Foo for S {
+ $0const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self) {
+ todo!()
+ }
+}"#,
+ );
+ // in the same module
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ fn bar(&self);
+ fn baz(&self);
}
+
+ struct S;
+
+ impl Foo for S {
+ fn bar(&self) {}
+$0
+ }
+}"#,
+ r#"
+mod m {
+ trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ fn bar(&self);
+ fn baz(&self);
+ }
+
+ struct S;
+
+ impl Foo for S {
+ fn bar(&self) {}
+
+ $0type Output;
+
+ const CONST_2: i32;
+
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self) {
+ todo!()
+ }
+
+ fn baz(&self) {
+ todo!()
+ }
+
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_add_default_preserves_indentation() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+mod m {
+ pub trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+ fn foo(some: u32) -> bool;
+ }
+}
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+ fn foo(some: u32) -> bool;
+ }
+}
+struct S;
+impl m::Foo for S {
+ $0const CONST: usize = 42;
+
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+}"#,
+ )
+ }
+
+ #[test]
+ fn nested_macro_should_not_cause_crash() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl $0AnotherTrait for () {
+}
+"#,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ $0fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+ todo!()
+ }
+}
+"#,
+ );
+ }
+
+ // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+ #[test]
+ fn paths_in_nested_macro_should_get_transformed() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl $0AnotherTrait<i32> for () {
+}
+"#,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ $0fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+ todo!()
}
}
"#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 5d81e8cfe..7384390f2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -148,7 +148,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
return None;
}
- let variants_of_enums = vec![variants.clone(); len];
+ let variants_of_enums = vec![variants; len];
let missing_pats = variants_of_enums
.into_iter()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
index 879c478ac..e5f0201bd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
@@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
if ty.is_unit() {
return None;
}
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?;
acc.add(
AssistId("add_return_type", AssistKind::RefactorRewrite),
@@ -34,8 +34,8 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|builder| {
match builder_edit_pos {
InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
- let preceeding_whitespace = if needs_whitespace { " " } else { "" };
- builder.insert(insert_pos, format!("{preceeding_whitespace}-> {ty} "))
+ let preceding_whitespace = if needs_whitespace { " " } else { "" };
+ builder.insert(insert_pos, format!("{preceding_whitespace}-> {ty} "))
}
InsertOrReplace::Replace(text_range) => {
builder.replace(text_range, format!("-> {ty}"))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 698ad78cc..7acf2ea0a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -132,7 +132,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
acc.add_group(
&group_label,
AssistId("auto_import", AssistKind::QuickFix),
- format!("Import `{import_path}`"),
+ format!("Import `{}`", import_path.display(ctx.db())),
range,
|builder| {
let scope = match scope.clone() {
@@ -203,7 +203,7 @@ fn relevance_score(
// get the distance between the imported path and the current module
// (prefer items that are more local)
Some((item_module, current_module)) => {
- score -= module_distance_hueristic(db, current_module, &item_module) as i32;
+ score -= module_distance_heuristic(db, current_module, &item_module) as i32;
}
// could not find relevant modules, so just use the length of the path as an estimate
@@ -214,7 +214,7 @@ fn relevance_score(
}
/// A heuristic that gives a higher score to modules that are more separated.
-fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
+fn module_distance_heuristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
// get the path starting from the item to the respective crate roots
let mut current_path = current.path_to_root(db);
let mut item_path = item.path_to_root(db);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
index db96ad330..1af52c592 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -160,7 +160,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
};
// Verify this is `bool::then` that is being called.
let func = ctx.sema.resolve_method_call(&mcall)?;
- if func.name(ctx.sema.db).to_string() != "then" {
+ if func.name(ctx.sema.db).display(ctx.db()).to_string() != "then" {
return None;
}
let assoc = func.as_assoc_item(ctx.sema.db)?;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 9e1d9a702..db96c8fe4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -119,7 +119,7 @@ pub(crate) fn convert_for_loop_with_for_each(
{
// We have either "for x in &col" and col implements a method called iter
// or "for x in &mut col" and col implements a method called iter_mut
- format_to!(buf, "{expr_behind_ref}.{method}()");
+ format_to!(buf, "{expr_behind_ref}.{}()", method.display(ctx.db()));
} else if let ast::Expr::RangeExpr(..) = iterable {
// range expressions need to be parenthesized for the syntax to be correct
format_to!(buf, "({iterable})");
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index c82a3b530..5f7056b9c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -5,6 +5,88 @@ use syntax::T;
use crate::{AssistContext, AssistId, AssistKind, Assists};
+// Assist: convert_let_else_to_match
+//
+// Converts let-else statement to let statement and match expression.
+//
+// ```
+// fn main() {
+// let Ok(mut x) = f() else$0 { return };
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let mut x = match f() {
+// Ok(x) => x,
+// _ => return,
+// };
+// }
+// ```
+pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ // should focus on else token to trigger
+ let let_stmt = ctx
+ .find_token_syntax_at_offset(T![else])
+ .and_then(|it| it.parent()?.parent())
+ .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
+ let let_stmt = LetStmt::cast(let_stmt)?;
+ let let_else_block = let_stmt.let_else()?.block_expr()?;
+ let let_init = let_stmt.initializer()?;
+ if let_stmt.ty().is_some() {
+ // don't support let with type annotation
+ return None;
+ }
+ let pat = let_stmt.pat()?;
+ let mut binders = Vec::new();
+ binders_in_pat(&mut binders, &pat, &ctx.sema)?;
+
+ let target = let_stmt.syntax().text_range();
+ acc.add(
+ AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
+ "Convert let-else to let and match",
+ target,
+ |edit| {
+ let indent_level = let_stmt.indent_level().0 as usize;
+ let indent = " ".repeat(indent_level);
+ let indent1 = " ".repeat(indent_level + 1);
+
+ let binders_str = binders_to_str(&binders, false);
+ let binders_str_mut = binders_to_str(&binders, true);
+
+ let init_expr = let_init.syntax().text();
+ let mut pat_no_mut = pat.syntax().text().to_string();
+ // remove the mut from the pattern
+ for (b, ismut) in binders.iter() {
+ if *ismut {
+ pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
+ }
+ }
+
+ let only_expr = let_else_block.statements().next().is_none();
+ let branch2 = match &let_else_block.tail_expr() {
+ Some(tail) if only_expr => format!("{tail},"),
+ _ => let_else_block.syntax().text().to_string(),
+ };
+ let replace = if binders.is_empty() {
+ format!(
+ "match {init_expr} {{
+{indent1}{pat_no_mut} => {binders_str}
+{indent1}_ => {branch2}
+{indent}}}"
+ )
+ } else {
+ format!(
+ "let {binders_str_mut} = match {init_expr} {{
+{indent1}{pat_no_mut} => {binders_str},
+{indent1}_ => {branch2}
+{indent}}};"
+ )
+ };
+ edit.replace(target, replace);
+ },
+ )
+}
+
/// Gets a list of binders in a pattern, and whether they are mut.
fn binders_in_pat(
acc: &mut Vec<(Name, bool)>,
@@ -97,85 +179,6 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
}
}
-// Assist: convert_let_else_to_match
-//
-// Converts let-else statement to let statement and match expression.
-//
-// ```
-// fn main() {
-// let Ok(mut x) = f() else$0 { return };
-// }
-// ```
-// ->
-// ```
-// fn main() {
-// let mut x = match f() {
-// Ok(x) => x,
-// _ => return,
-// };
-// }
-// ```
-pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- // should focus on else token to trigger
- let else_token = ctx.find_token_syntax_at_offset(T![else])?;
- let let_stmt = LetStmt::cast(else_token.parent()?.parent()?)?;
- let let_else_block = let_stmt.let_else()?.block_expr()?;
- let let_init = let_stmt.initializer()?;
- if let_stmt.ty().is_some() {
- // don't support let with type annotation
- return None;
- }
- let pat = let_stmt.pat()?;
- let mut binders = Vec::new();
- binders_in_pat(&mut binders, &pat, &ctx.sema)?;
-
- let target = let_stmt.syntax().text_range();
- acc.add(
- AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
- "Convert let-else to let and match",
- target,
- |edit| {
- let indent_level = let_stmt.indent_level().0 as usize;
- let indent = " ".repeat(indent_level);
- let indent1 = " ".repeat(indent_level + 1);
-
- let binders_str = binders_to_str(&binders, false);
- let binders_str_mut = binders_to_str(&binders, true);
-
- let init_expr = let_init.syntax().text();
- let mut pat_no_mut = pat.syntax().text().to_string();
- // remove the mut from the pattern
- for (b, ismut) in binders.iter() {
- if *ismut {
- pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
- }
- }
-
- let only_expr = let_else_block.statements().next().is_none();
- let branch2 = match &let_else_block.tail_expr() {
- Some(tail) if only_expr => format!("{tail},"),
- _ => let_else_block.syntax().text().to_string(),
- };
- let replace = if binders.is_empty() {
- format!(
- "match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str}
-{indent1}_ => {branch2}
-{indent}}}"
- )
- } else {
- format!(
- "let {binders_str_mut} = match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str},
-{indent1}_ => {branch2}
-{indent}}};"
- )
- };
- edit.replace(target, replace);
- },
- )
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index 7f2c01772..fc6236a17 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -16,7 +16,7 @@ use crate::{
// ```
// # //- minicore: option
// fn foo(opt: Option<()>) {
-// let val = $0match opt {
+// let val$0 = match opt {
// Some(it) => it,
// None => return,
// };
@@ -30,7 +30,10 @@ use crate::{
// ```
pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
- let binding = let_stmt.pat()?;
+ let pat = let_stmt.pat()?;
+ if ctx.offset() > pat.syntax().text_range().end() {
+ return None;
+ }
let Some(ast::Expr::MatchExpr(initializer)) = let_stmt.initializer() else { return None };
let initializer_expr = initializer.expr()?;
@@ -56,7 +59,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
let_stmt.syntax().text_range(),
|builder| {
let extracting_arm_pat =
- rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding);
+ rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat);
builder.replace(
let_stmt.syntax().text_range(),
format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
@@ -161,7 +164,7 @@ mod tests {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => (),
};
@@ -211,7 +214,7 @@ fn foo(opt: Option<Foo>) -> Result<u32, ()> {
r#"
//- minicore: option
fn foo(opt: Option<i32>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it + 1,
None => return,
};
@@ -224,7 +227,7 @@ fn foo(opt: Option<i32>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => {
let _ = 1 + 1;
it
@@ -244,7 +247,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) if 2 > 1 => it,
None => return,
};
@@ -260,7 +263,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -281,7 +284,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let ref mut val = $0match opt {
+ let ref mut val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -302,7 +305,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option, result
fn foo(opt: Option<Result<()>>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(Ok(it)) => it,
_ => return,
};
@@ -324,7 +327,7 @@ fn foo(opt: Option<Result<()>>) {
//- minicore: option
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => break,
};
@@ -346,7 +349,7 @@ fn foo(opt: Option<()>) {
//- minicore: option
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => continue,
};
@@ -370,7 +373,7 @@ fn panic() -> ! {}
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => panic(),
};
@@ -401,7 +404,7 @@ struct Point {
}
fn foo(opt: Option<Point>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(Point { x: 0, y }) => y,
_ => return,
};
@@ -427,7 +430,7 @@ fn foo(opt: Option<Point>) {
r#"
//- minicore: option
fn foo(opt: Option<i32>) -> Option<i32> {
- let val = $0match opt {
+ let val$0 = match opt {
it @ Some(42) => it,
_ => return None,
};
@@ -450,7 +453,7 @@ fn foo(opt: Option<i32>) -> Option<i32> {
r#"
//- minicore: option
fn f() {
- let (x, y) = $0match Some((0, 1)) {
+ let (x, y)$0 = match Some((0, 1)) {
Some(it) => it,
None => return,
};
@@ -471,7 +474,7 @@ fn f() {
r#"
//- minicore: option
fn f() {
- let x = $0match Some(()) {
+ let x$0 = match Some(()) {
Some(it) => it,
None => {//comment
println!("nope");
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 9dc1da246..fe1cb6fce 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -1,9 +1,9 @@
use either::Either;
-use ide_db::defs::Definition;
+use ide_db::{defs::Definition, search::FileReference};
use itertools::Itertools;
use syntax::{
ast::{self, AstNode, HasGenericParams, HasVisibility},
- match_ast, SyntaxKind, SyntaxNode,
+ match_ast, SyntaxKind,
};
use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -52,7 +52,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
- let strukt = ctx.find_node_at_offset::<Either<ast::Struct, ast::Variant>>()?;
+ // XXX: We don't currently provide this assist for struct definitions inside macros, but if we
+ // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files
+ // too.
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
let record_fields = match field_list {
ast::FieldList::RecordFieldList(it) => it,
@@ -62,12 +66,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
};
- let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
acc.add(
AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
"Convert to tuple struct",
- target,
+ strukt.syntax().text_range(),
|edit| {
edit_field_references(ctx, edit, record_fields.fields());
edit_struct_references(ctx, edit, strukt_def);
@@ -82,6 +85,8 @@ fn edit_struct_def(
strukt: &Either<ast::Struct, ast::Variant>,
record_fields: ast::RecordFieldList,
) {
+ // Note that we don't need to consider macro files in this function because this this is
+ // currently not triggered for struct definitions inside macro calls.
let tuple_fields = record_fields
.fields()
.filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
@@ -137,50 +142,72 @@ fn edit_struct_references(
};
let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
- let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
- match_ast! {
- match node {
- ast::RecordPat(record_struct_pat) => {
- edit.replace(
- record_struct_pat.syntax().text_range(),
- ast::make::tuple_struct_pat(
- record_struct_pat.path()?,
- record_struct_pat
- .record_pat_field_list()?
- .fields()
- .filter_map(|pat| pat.pat())
- )
- .to_string()
- );
- },
- ast::RecordExpr(record_expr) => {
- let path = record_expr.path()?;
- let args = record_expr
- .record_expr_field_list()?
- .fields()
- .filter_map(|f| f.expr())
- .join(", ");
-
- edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
- },
- _ => return None,
- }
- }
- Some(())
- };
-
for (file_id, refs) in usages {
edit.edit_file(file_id);
for r in refs {
- for node in r.name.syntax().ancestors() {
- if edit_node(edit, node).is_some() {
- break;
- }
- }
+ process_struct_name_reference(ctx, r, edit);
}
}
}
+fn process_struct_name_reference(
+ ctx: &AssistContext<'_>,
+ r: FileReference,
+ edit: &mut SourceChangeBuilder,
+) -> Option<()> {
+ // First check if it's the last semgnet of a path that directly belongs to a record
+ // expression/pattern.
+ let name_ref = r.name.as_name_ref()?;
+ let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
+ // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
+ let full_path =
+ path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap();
+
+ if full_path.segment().unwrap().name_ref()? != *name_ref {
+ // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
+ // struct we want to edit.
+ return None;
+ }
+
+ let parent = full_path.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::RecordPat(record_struct_pat) => {
+ // When we failed to get the original range for the whole struct expression node,
+ // we can't provide any reasonable edit. Leave it untouched.
+ let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?;
+ edit.replace(
+ file_range.range,
+ ast::make::tuple_struct_pat(
+ record_struct_pat.path()?,
+ record_struct_pat
+ .record_pat_field_list()?
+ .fields()
+ .filter_map(|pat| pat.pat())
+ )
+ .to_string()
+ );
+ },
+ ast::RecordExpr(record_expr) => {
+ // When we failed to get the original range for the whole struct pattern node,
+ // we can't provide any reasonable edit. Leave it untouched.
+ let file_range = ctx.sema.original_range_opt(record_expr.syntax())?;
+ let path = record_expr.path()?;
+ let args = record_expr
+ .record_expr_field_list()?
+ .fields()
+ .filter_map(|f| f.expr())
+ .join(", ");
+
+ edit.replace(file_range.range, format!("{path}({args})"));
+ },
+ _ => {}
+ }
+ }
+
+ Some(())
+}
+
fn edit_field_references(
ctx: &AssistContext<'_>,
edit: &mut SourceChangeBuilder,
@@ -199,7 +226,7 @@ fn edit_field_references(
if let Some(name_ref) = r.name.as_name_ref() {
// Only edit the field reference if it's part of a `.field` access
if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
- edit.replace(name_ref.syntax().text_range(), index.to_string());
+ edit.replace(r.range, index.to_string());
}
}
}
@@ -816,4 +843,139 @@ fn f() {
"#,
);
}
+
+ #[test]
+ fn field_access_inside_macro_call() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct $0Struct {
+ inner: i32,
+}
+
+macro_rules! id {
+ ($e:expr) => { $e }
+}
+
+fn test(c: Struct) {
+ id!(c.inner);
+}
+"#,
+ r#"
+struct Struct(i32);
+
+macro_rules! id {
+ ($e:expr) => { $e }
+}
+
+fn test(c: Struct) {
+ id!(c.0);
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn struct_usage_inside_macro_call() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+macro_rules! id {
+ ($($t:tt)*) => { $($t)* }
+}
+
+struct $0Struct {
+ inner: i32,
+}
+
+fn test() {
+ id! {
+ let s = Struct {
+ inner: 42,
+ };
+ let Struct { inner: value } = s;
+ let Struct { inner } = s;
+ }
+}
+"#,
+ r#"
+macro_rules! id {
+ ($($t:tt)*) => { $($t)* }
+}
+
+struct Struct(i32);
+
+fn test() {
+ id! {
+ let s = Struct(42);
+ let Struct(value) = s;
+ let Struct(inner) = s;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn struct_name_ref_may_not_be_part_of_struct_expr_or_struct_pat() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct $0Struct {
+ inner: i32,
+}
+struct Outer<T> {
+ value: T,
+}
+fn foo<T>() -> T { loop {} }
+
+fn test() {
+ Outer {
+ value: foo::<Struct>();
+ }
+}
+
+trait HasAssoc {
+ type Assoc;
+ fn test();
+}
+impl HasAssoc for Struct {
+ type Assoc = Outer<i32>;
+ fn test() {
+ let a = Self::Assoc {
+ value: 42,
+ };
+ let Self::Assoc { value } = a;
+ }
+}
+"#,
+ r#"
+struct Struct(i32);
+struct Outer<T> {
+ value: T,
+}
+fn foo<T>() -> T { loop {} }
+
+fn test() {
+ Outer {
+ value: foo::<Struct>();
+ }
+}
+
+trait HasAssoc {
+ type Assoc;
+ fn test();
+}
+impl HasAssoc for Struct {
+ type Assoc = Outer<i32>;
+ fn test() {
+ let a = Self::Assoc {
+ value: 42,
+ };
+ let Self::Assoc { value } = a;
+ }
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
new file mode 100644
index 000000000..399f87c8f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
@@ -0,0 +1,209 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::ast::{self, HasGenericParams, HasName};
+use syntax::{AstNode, SyntaxKind};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: convert_nested_function_to_closure
+//
+// Converts a function that is defined within the body of another function into a closure.
+//
+// ```
+// fn main() {
+// fn fo$0o(label: &str, number: u64) {
+// println!("{}: {}", label, number);
+// }
+//
+// foo("Bar", 100);
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let foo = |label: &str, number: u64| {
+// println!("{}: {}", label, number);
+// };
+//
+// foo("Bar", 100);
+// }
+// ```
+pub(crate) fn convert_nested_function_to_closure(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let function = name.syntax().parent().and_then(ast::Fn::cast)?;
+
+ if !is_nested_function(&function) || is_generic(&function) || has_modifiers(&function) {
+ return None;
+ }
+
+ let target = function.syntax().text_range();
+ let body = function.body()?;
+ let name = function.name()?;
+ let param_list = function.param_list()?;
+
+ acc.add(
+ AssistId("convert_nested_function_to_closure", AssistKind::RefactorRewrite),
+ "Convert nested function to closure",
+ target,
+ |edit| {
+ let params = &param_list.syntax().text().to_string();
+ let params = params.strip_prefix("(").unwrap_or(params);
+ let params = params.strip_suffix(")").unwrap_or(params);
+
+ let mut body = body.to_string();
+ if !has_semicolon(&function) {
+ body.push(';');
+ }
+ edit.replace(target, format!("let {name} = |{params}| {body}"));
+ },
+ )
+}
+
+/// Returns whether the given function is nested within the body of another function.
+fn is_nested_function(function: &ast::Fn) -> bool {
+ function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| {
+ matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_))
+ })
+}
+
+/// Returns whether the given nested function has generic parameters.
+fn is_generic(function: &ast::Fn) -> bool {
+ function.generic_param_list().is_some()
+}
+
+/// Returns whether the given nested function has any modifiers:
+///
+/// - `async`,
+/// - `const` or
+/// - `unsafe`
+fn has_modifiers(function: &ast::Fn) -> bool {
+ function.async_token().is_some()
+ || function.const_token().is_some()
+ || function.unsafe_token().is_some()
+}
+
+/// Returns whether the given nested function has a trailing semicolon.
+fn has_semicolon(function: &ast::Fn) -> bool {
+ function
+ .syntax()
+ .next_sibling_or_token()
+ .map(|t| t.kind() == SyntaxKind::SEMICOLON)
+ .unwrap_or(false)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::convert_nested_function_to_closure;
+
+ #[test]
+ fn convert_nested_function_to_closure_works() {
+ check_assist(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn $0foo(a: u64, b: u64) -> u64 {
+ 2 * (a + b)
+ }
+
+ _ = foo(3, 4);
+}
+ "#,
+ r#"
+fn main() {
+ let foo = |a: u64, b: u64| {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_works_with_existing_semicolon() {
+ check_assist(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn foo$0(a: u64, b: u64) -> u64 {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ r#"
+fn main() {
+ let foo = |a: u64, b: u64| {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_on_top_level_function() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn ma$0in() {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_when_cursor_off_name() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn foo(a: u64, $0b: u64) -> u64 {
+ 2 * (a + b)
+ }
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_if_function_has_generic_params() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn fo$0o<S: Into<String>>(s: S) -> String {
+ s.into()
+ }
+
+ _ = foo("hello");
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_if_function_has_modifier() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ const fn fo$0o(s: String) -> String {
+ s
+ }
+
+ _ = foo("hello");
+}
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index b97be34c5..dcb96ab8a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -504,7 +504,7 @@ fn main() {
}
#[test]
- fn ignore_statements_aftert_if() {
+ fn ignore_statements_after_if() {
check_assist_not_applicable(
convert_to_guarded_return,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 772e032fb..017853a4a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -50,7 +50,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
- let strukt = ctx.find_node_at_offset::<Either<ast::Struct, ast::Variant>>()?;
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
let tuple_fields = match field_list {
ast::FieldList::TupleFieldList(it) => it,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 31c2ce7c1..ea71d165e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -91,7 +91,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
return None;
}
- let ty = ctx.sema.type_of_pat(&ident_pat.clone().into())?.adjusted();
+ let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?;
let ref_type = if ty.is_mutable_reference() {
Some(RefType::Mutable)
} else if ty.is_reference() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
index 87f5018fb..5c435dd9c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -65,7 +65,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
- let path = make::ext::ident_path(&n.to_string());
+ let path = make::ext::ident_path(&n.display(ctx.db()).to_string());
make::use_tree(path, None, None, false)
}))
.clone_for_update();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 0b90c9ba3..2a67909e6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let node = ctx.covering_element();
+ if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
+ cov_mark::hit!(extract_function_in_braces_is_not_applicable);
+ return None;
+ }
+
if node.kind() == COMMENT {
cov_mark::hit!(extract_function_in_comment_is_not_applicable);
return None;
@@ -178,7 +183,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
let mut names_in_scope = vec![];
- semantics_scope.process_all_names(&mut |name, _| names_in_scope.push(name.to_string()));
+ semantics_scope.process_all_names(&mut |name, _| {
+ names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string())
+ });
let default_name = "fun_name";
@@ -369,7 +376,7 @@ struct OutlivedLocal {
/// Container of local variable usages
///
-/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
+/// Semantically same as `UsageSearchResult`, but provides more convenient interface
struct LocalUsages(ide_db::search::UsageSearchResult);
impl LocalUsages {
@@ -438,7 +445,7 @@ impl Param {
}
fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param {
- let var = self.var.name(ctx.db()).to_string();
+ let var = self.var.name(ctx.db()).display(ctx.db()).to_string();
let var_name = make::name(&var);
let pat = match self.kind() {
ParamKind::MutValue => make::ident_pat(false, true, var_name),
@@ -468,7 +475,8 @@ impl TryKind {
let name = adt.name(ctx.db());
// FIXME: use lang items to determine if it is std type or user defined
// E.g. if user happens to define type named `Option`, we would have false positive
- match name.to_string().as_str() {
+ let name = &name.display(ctx.db()).to_string();
+ match name.as_str() {
"Option" => Some(TryKind::Option),
"Result" => Some(TryKind::Result { ty }),
_ => None,
@@ -702,7 +710,7 @@ impl FunctionBody {
) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
let mut self_param = None;
let mut res = FxIndexSet::default();
- let mut cb = |name_ref: Option<_>| {
+ let mut add_name_if_local = |name_ref: Option<_>| {
let local_ref =
match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
Some(
@@ -726,21 +734,24 @@ impl FunctionBody {
};
self.walk_expr(&mut |expr| match expr {
ast::Expr::PathExpr(path_expr) => {
- cb(path_expr.path().and_then(|it| it.as_single_name_ref()))
+ add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref()))
}
ast::Expr::ClosureExpr(closure_expr) => {
if let Some(body) = closure_expr.body() {
- body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it));
+ body.syntax()
+ .descendants()
+ .map(ast::NameRef::cast)
+ .for_each(&mut add_name_if_local);
}
}
ast::Expr::MacroExpr(expr) => {
if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) {
tt.syntax()
- .children_with_tokens()
- .flat_map(SyntaxElement::into_token)
- .filter(|it| it.kind() == SyntaxKind::IDENT)
+ .descendants_with_tokens()
+ .filter_map(SyntaxElement::into_token)
+ .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
.flat_map(|t| sema.descend_into_macros(t))
- .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast)));
+ .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
}
}
_ => (),
@@ -1286,8 +1297,8 @@ fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> {
let as_impl = ast::Impl::cast(trait_impl.clone())?;
let impl_type = Some(impl_type_name(&as_impl)?);
- let sibblings = trait_impl.parent()?.children();
- sibblings
+ let siblings = trait_impl.parent()?.children();
+ siblings
.filter_map(ast::Impl::cast)
.find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
}
@@ -1333,14 +1344,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St
[var] => {
let modifier = mut_modifier(var);
let name = var.local.name(ctx.db());
- format_to!(buf, "let {modifier}{name} = ")
+ format_to!(buf, "let {modifier}{} = ", name.display(ctx.db()))
}
vars => {
buf.push_str("let (");
let bindings = vars.iter().format_with(", ", |local, f| {
let modifier = mut_modifier(local);
let name = local.local.name(ctx.db());
- f(&format_args!("{modifier}{name}"))
+ f(&format_args!("{modifier}{}", name.display(ctx.db())))?;
+ Ok(())
});
format_to!(buf, "{bindings}");
buf.push_str(") = ");
@@ -1479,7 +1491,7 @@ impl FlowHandler {
}
fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr {
- let name = var.name(ctx.db()).to_string();
+ let name = var.name(ctx.db()).display(ctx.db()).to_string();
make::expr_path(make::ext::ident_path(&name))
}
@@ -1879,7 +1891,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
}
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
- ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string())
+ ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
}
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
@@ -4340,6 +4352,82 @@ fn $0fun_name(n: i32) -> i32 {
}
#[test]
+ fn param_usage_in_macro_with_nested_tt() {
+ check_assist(
+ extract_function,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+fn foo() {
+ let n = 1;
+ let t = 1;
+ $0let k = n * m!((n) + { t });$0
+ let m = k + 1;
+}
+"#,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+fn foo() {
+ let n = 1;
+ let t = 1;
+ let k = fun_name(n, t);
+ let m = k + 1;
+}
+
+fn $0fun_name(n: i32, t: i32) -> i32 {
+ let k = n * m!((n) + { t });
+ k
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn param_usage_in_macro_with_nested_tt_2() {
+ check_assist(
+ extract_function,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+struct S(i32);
+impl S {
+ fn foo(&self) {
+ let n = 1;
+ $0let k = n * m!((n) + { self.0 });$0
+ let m = k + 1;
+ }
+}
+"#,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+struct S(i32);
+impl S {
+ fn foo(&self) {
+ let n = 1;
+ let k = self.fun_name(n);
+ let m = k + 1;
+ }
+
+ fn $0fun_name(&self, n: i32) -> i32 {
+ let k = n * m!((n) + { self.0 });
+ k
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn extract_with_await() {
check_assist(
extract_function,
@@ -4640,6 +4728,7 @@ const fn $0fun_name() {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
let mut x = 5;
for _ in 0..10 {
@@ -4663,6 +4752,7 @@ fn $0fun_name(x: &mut i32) {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
for _ in 0..10 {
let mut x = 5;
@@ -4686,6 +4776,7 @@ fn $0fun_name(mut x: i32) {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
loop {
let mut x = 5;
@@ -5388,6 +5479,30 @@ fn $0fun_name<T: Debug>(i: T) {
}
#[test]
+ fn dont_emit_type_with_hidden_lifetime_parameter() {
+ // FIXME: We should emit a `<T: Debug>` generic argument for the generated function
+ check_assist(
+ extract_function,
+ r#"
+struct Struct<'a, T>(&'a T);
+fn func<T: Debug>(i: Struct<'_, T>) {
+ $0foo(i);$0
+}
+"#,
+ r#"
+struct Struct<'a, T>(&'a T);
+fn func<T: Debug>(i: Struct<'_, T>) {
+ fun_name(i);
+}
+
+fn $0fun_name(i: Struct<'_, T>) {
+ foo(i);
+}
+"#,
+ );
+ }
+
+ #[test]
fn preserve_generics_from_body() {
check_assist(
extract_function,
@@ -5800,4 +5915,40 @@ fn $0fun_name() -> ControlFlow<()> {
"#,
);
}
+
+ #[test]
+ fn in_left_curly_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
+ }
+
+ #[test]
+ fn in_right_curly_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
+ }
+
+ #[test]
+ fn in_left_paren_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
+ }
+
+ #[test]
+ fn in_right_paren_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
+ }
+
+ #[test]
+ fn in_left_brack_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
+ }
+
+ #[test]
+ fn in_right_brack_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
index 0fa7bd558..de37f5f13 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
@@ -357,7 +357,7 @@ impl Module {
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
let (mut replacements, record_field_parents, impls) =
- get_replacements_for_visibilty_change(&mut self.body_items, false);
+ get_replacements_for_visibility_change(&mut self.body_items, false);
let mut impl_items: Vec<ast::Item> = impls
.into_iter()
@@ -366,7 +366,7 @@ impl Module {
.collect();
let (mut impl_item_replacements, _, _) =
- get_replacements_for_visibilty_change(&mut impl_items, true);
+ get_replacements_for_visibility_change(&mut impl_items, true);
replacements.append(&mut impl_item_replacements);
@@ -824,7 +824,7 @@ fn does_source_exists_outside_sel_in_same_mod(
source_exists_outside_sel_in_same_mod
}
-fn get_replacements_for_visibilty_change(
+fn get_replacements_for_visibility_change(
items: &mut [ast::Item],
is_clone_for_updated: bool,
) -> (
@@ -904,7 +904,7 @@ fn compare_hir_and_ast_module(
) -> Option<()> {
let hir_mod_name = hir_module.name(ctx.db())?;
let ast_mod_name = ast_module.name()?;
- if hir_mod_name.to_string() != ast_mod_name.to_string() {
+ if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() {
return None;
}
@@ -1236,7 +1236,8 @@ mod modname {
}
#[test]
- fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
+ fn test_extract_module_for_corresponding_adt_of_impl_present_in_same_mod_but_not_in_selection()
+ {
check_assist(
extract_module,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 49debafe1..e4f64ccc7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -158,7 +158,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
),
_ => false,
})
- .any(|(name, _)| name.to_string() == variant_name.to_string())
+ .any(|(name, _)| name.display(db).to_string() == variant_name.to_string())
}
fn extract_generic_params(
@@ -1006,7 +1006,7 @@ enum X<'a, 'b, 'x> {
}
#[test]
- fn test_extract_struct_with_liftime_type_const() {
+ fn test_extract_struct_with_lifetime_type_const() {
check_assist(
extract_struct_from_enum_variant,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
index b310c2db9..b6e7d6209 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,6 +1,9 @@
use either::Either;
use ide_db::syntax_helpers::node_ext::walk_ty;
-use syntax::ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName};
+use syntax::{
+ ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
+ ted,
+};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -34,14 +37,16 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|| item.syntax(),
|impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax),
);
- let insert_pos = node.text_range().start();
let target = ty.syntax().text_range();
acc.add(
AssistId("extract_type_alias", AssistKind::RefactorExtract),
"Extract type as type alias",
target,
- |builder| {
+ |edit| {
+ let node = edit.make_syntax_mut(node.clone());
+ let target_ty = edit.make_mut(ty.clone());
+
let mut known_generics = match item.generic_param_list() {
Some(it) => it.generic_params().collect(),
None => Vec::new(),
@@ -56,27 +61,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let generic_params =
generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
+ // Replace original type with the alias
let ty_args = generic_params
.as_ref()
.map_or(String::new(), |it| it.to_generic_args().to_string());
- let replacement = format!("Type{ty_args}");
- builder.replace(target, replacement);
-
- let indent = IndentLevel::from_node(node);
- let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
- match ctx.config.snippet_cap {
- Some(cap) => {
- builder.insert_snippet(
- cap,
- insert_pos,
- format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
- );
- }
- None => {
- builder.insert(
- insert_pos,
- format!("type Type{generic_params} = {ty};\n\n{indent}"),
- );
+ // FIXME: replace with a `ast::make` constructor
+ let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update();
+ ted::replace(target_ty.syntax(), new_ty.syntax());
+
+ // Insert new alias
+ let indent = IndentLevel::from_node(&node);
+ let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None)))
+ .clone_for_update();
+ ted::insert_all(
+ ted::Position::before(node),
+ vec![
+ ty_alias.syntax().clone().into(),
+ make::tokens::whitespace(&format!("\n\n{indent}")).into(),
+ ],
+ );
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(name) = ty_alias.name() {
+ edit.add_tabstop_before(cap, name);
}
}
},
@@ -151,7 +158,7 @@ fn collect_used_generics<'gp>(
.and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
),
ast::Type::ArrayType(ar) => {
- if let Some(ast::Expr::PathExpr(p)) = ar.expr() {
+ if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) {
if let Some(path) = p.path() {
if let Some(name_ref) = path.as_single_name_ref() {
if let Some(param) = known_generics.iter().find(|gp| {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 163561412..014c23197 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,3 +1,4 @@
+use hir::TypeInfo;
use stdx::format_to;
use syntax::{
ast::{self, AstNode},
@@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
.find_map(valid_target_expr)?;
- if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
- if ty_info.adjusted().is_unit() {
- return None;
- }
+ let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
+ if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
+ return None;
}
- let reference_modifier = match get_receiver_type(ctx, &to_extract) {
+ let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
+ let needs_adjust = parent
+ .as_ref()
+ .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
+
+ let reference_modifier = match ty.filter(|_| needs_adjust) {
Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
Some(receiver_type) if receiver_type.is_reference() => "&",
_ => "",
};
- let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
- let var_modifier = match parent_ref_expr {
- Some(expr) if expr.mut_token().is_some() => "mut ",
+ let var_modifier = match parent {
+ Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
_ => "",
};
@@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
}
}
-fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
- let receiver = get_receiver(expression.clone())?;
- Some(ctx.sema.type_of_expr(&receiver)?.original())
-}
-
-/// In the expression `a.b.c.x()`, find `a`
-fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
- match expression {
- ast::Expr::FieldExpr(field) if field.expr().is_some() => {
- let nested_expression = &field.expr()?;
- get_receiver(nested_expression.to_owned())
- }
- _ => Some(expression),
- }
-}
-
#[derive(Debug)]
enum Anchor {
Before(SyntaxNode),
@@ -944,6 +932,11 @@ struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
+
fn foo(s: &mut S) {
$0s.vec$0.push(0);
}"#,
@@ -952,6 +945,11 @@ struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
+
fn foo(s: &mut S) {
let $0vec = &mut s.vec;
vec.push(0);
@@ -973,6 +971,10 @@ struct X {
struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
fn foo(f: &mut Y) {
$0f.field.field.vec$0.push(0);
@@ -987,6 +989,10 @@ struct X {
struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec;
@@ -1123,7 +1129,7 @@ struct S {
}
fn foo(s: S) {
- let $0x = s.sub;
+ let $0x = &s.sub;
x.do_thing();
}"#,
);
@@ -1189,7 +1195,7 @@ impl X {
fn foo() {
let local = &mut S::new();
- let $0x = &mut local.sub;
+ let $0x = &local.sub;
x.do_thing();
}"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index 4c61678ea..d6c59a9c8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -62,7 +62,9 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
let assist_label = match target_name {
None => format!("Change visibility to {missing_visibility}"),
- Some(name) => format!("Change visibility of {name} to {missing_visibility}"),
+ Some(name) => {
+ format!("Change visibility of {} to {missing_visibility}", name.display(ctx.db()))
+ }
};
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
@@ -117,8 +119,11 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
let target_file = in_file_source.file_id.original_file(ctx.db());
let target_name = record_field_def.name(ctx.db());
- let assist_label =
- format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}");
+ let assist_label = format!(
+ "Change visibility of {}.{} to {missing_visibility}",
+ parent_name.display(ctx.db()),
+ target_name.display(ctx.db())
+ );
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
builder.edit_file(target_file);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
index ccdfcb0d9..eccd7675f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
@@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let ty = ctx.sema.type_of_expr(&expr)?;
let scope = ctx.sema.scope(statement.syntax())?;
let constant_module = scope.module();
- let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?;
+ let type_name =
+ ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?;
let target = statement.syntax().parent()?.text_range();
let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -106,10 +107,10 @@ fn get_text_for_generate_constant(
let mut text = format!("{vis}const {constant_token}: {type_name} = $0;");
while let Some(name_ref) = not_exist_name_ref.pop() {
let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
- text = text.replace("\n", "\n ");
+ text = text.replace('\n', "\n ");
text = format!("{vis}mod {name_ref} {{{text}\n}}");
}
- Some(text.replace("\n", &format!("\n{indent}")))
+ Some(text.replace('\n', &format!("\n{indent}")))
}
fn target_data_for_generate_constant(
@@ -131,7 +132,7 @@ fn target_data_for_generate_constant(
let siblings_has_newline = l_curly_token
.siblings_with_tokens(Direction::Next)
- .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains("\n"))
+ .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains('\n'))
.is_some();
let post_string =
if siblings_has_newline { format!("{indent}") } else { format!("\n{indent}") };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index ed1b8f4e2..b68c766e6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use hir::{self, HasCrate, HasSource, HasVisibility};
use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _};
@@ -63,25 +65,34 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
- let krate = sema_field_ty.krate(ctx.db());
let mut methods = vec![];
- sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| {
- if let hir::AssocItem::Function(f) = item {
- if f.self_param(ctx.db()).is_some() && f.is_visible_from(ctx.db(), current_module) {
- methods.push(f)
- }
- }
- Option::<()>::None
- });
+ let mut seen_names = HashSet::new();
- for method in methods {
+ for ty in sema_field_ty.autoderef(ctx.db()) {
+ let krate = ty.krate(ctx.db());
+ ty.iterate_assoc_items(ctx.db(), krate, |item| {
+ if let hir::AssocItem::Function(f) = item {
+ let name = f.name(ctx.db());
+ if f.self_param(ctx.db()).is_some()
+ && f.is_visible_from(ctx.db(), current_module)
+ && seen_names.insert(name.clone())
+ {
+ methods.push((name, f))
+ }
+ }
+ Option::<()>::None
+ });
+ }
+ methods.sort_by(|(a, _), (b, _)| a.cmp(b));
+ for (name, method) in methods {
let adt = ast::Adt::Struct(strukt.clone());
- let name = method.name(ctx.db()).to_string();
- let impl_def = find_struct_impl(ctx, &adt, &[name]).flatten();
+ let name = name.display(ctx.db()).to_string();
+ // if `find_struct_impl` returns None, that means that a function named `name` already exists.
+ let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; };
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
- format!("Generate delegate for `{field_name}.{}()`", method.name(ctx.db())),
+ format!("Generate delegate for `{field_name}.{name}()`",),
target,
|builder| {
// Create the function
@@ -89,9 +100,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(source) => source.value,
None => return,
};
- let method_name = method.name(ctx.db());
let vis = method_source.visibility();
- let name = make::name(&method.name(ctx.db()).to_string());
+ let fn_name = make::name(&name);
let params =
method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
let type_params = method_source.generic_param_list();
@@ -101,17 +111,30 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let tail_expr = make::expr_method_call(
make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
- make::name_ref(&method_name.to_string()),
+ make::name_ref(&name),
arg_list,
);
let ret_type = method_source.ret_type();
let is_async = method_source.async_token().is_some();
+ let is_const = method_source.const_token().is_some();
+ let is_unsafe = method_source.unsafe_token().is_some();
let tail_expr_finished =
if is_async { make::expr_await(tail_expr) } else { tail_expr };
let body = make::block_expr([], Some(tail_expr_finished));
- let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async)
- .indent(ast::edit::IndentLevel(1))
- .clone_for_update();
+ let f = make::fn_(
+ vis,
+ fn_name,
+ type_params,
+ None,
+ params,
+ body,
+ ret_type,
+ is_async,
+ is_const,
+ is_unsafe,
+ )
+ .indent(ast::edit::IndentLevel(1))
+ .clone_for_update();
let cursor = Cursor::Before(f.syntax());
@@ -143,8 +166,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let name = &strukt_name.to_string();
let params = strukt.generic_param_list();
let ty_params = params.clone();
- let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params)
- .clone_for_update();
+ let where_clause = strukt.where_clause();
+
+ let impl_def = make::impl_(
+ ty_params,
+ None,
+ make::ty_path(make::ext::ident_path(name)),
+ where_clause,
+ None,
+ )
+ .clone_for_update();
let assoc_items = impl_def.get_or_create_assoc_item_list();
assoc_items.add_item(f.clone().into());
@@ -315,6 +346,44 @@ impl<T> Person<T> {
}
#[test]
+ fn test_generates_delegate_autoderef() {
+ check_assist(
+ generate_delegate_methods,
+ r#"
+//- minicore: deref
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ ag$0e: AgeDeref,
+}
+impl Person {}"#,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ age: AgeDeref,
+}
+impl Person {
+ $0fn age(&self) -> u8 {
+ self.age.age()
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn test_generate_delegate_visibility() {
check_assist_not_applicable(
generate_delegate_methods,
@@ -333,4 +402,26 @@ struct Person {
}"#,
)
}
+
+ #[test]
+ fn test_generate_not_eligible_if_fn_exists() {
+ check_assist_not_applicable(
+ generate_delegate_methods,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+
+struct Person {
+ ag$0e: Age,
+}
+impl Person {
+ fn age(&self) -> u8 { 0 }
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
index b6958e291..815453961 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
@@ -70,6 +70,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
target,
|edit| {
generate_edit(
+ ctx.db(),
edit,
strukt,
field_type.syntax(),
@@ -109,6 +110,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
target,
|edit| {
generate_edit(
+ ctx.db(),
edit,
strukt,
field_type.syntax(),
@@ -121,6 +123,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
}
fn generate_edit(
+ db: &RootDatabase,
edit: &mut SourceChangeBuilder,
strukt: ast::Struct,
field_type_syntax: &SyntaxNode,
@@ -144,7 +147,8 @@ fn generate_edit(
),
};
let strukt_adt = ast::Adt::Struct(strukt);
- let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
+ let deref_impl =
+ generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code);
edit.insert(start_offset, deref_impl);
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
index 339245b94..78ac2eb30 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
@@ -1,5 +1,5 @@
use syntax::{
- ast::{self, AstNode, HasAttrs},
+ ast::{self, edit::IndentLevel, AstNode, HasAttrs},
SyntaxKind::{COMMENT, WHITESPACE},
TextSize,
};
@@ -42,7 +42,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
.next();
match derive_attr {
None => {
- builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
+ let indent_level = IndentLevel::from_node(nominal.syntax());
+ builder.insert_snippet(
+ cap,
+ node_start,
+ format!("#[derive($0)]\n{indent_level}"),
+ );
}
Some(tt) => {
// Just move the cursor.
@@ -84,6 +89,20 @@ mod tests {
"struct Foo { $0 a: i32, }",
"#[derive($0)]\nstruct Foo { a: i32, }",
);
+ check_assist(
+ generate_derive,
+ "
+mod m {
+ struct Foo { a: i32,$0 }
+}
+ ",
+ "
+mod m {
+ #[derive($0)]
+ struct Foo { a: i32, }
+}
+ ",
+ );
}
#[test]
@@ -111,6 +130,24 @@ struct Foo { a: i32$0, }
struct Foo { a: i32, }
",
);
+ check_assist(
+ generate_derive,
+ "
+mod m {
+ /// `Foo` is a pretty important struct.
+ /// It does stuff.
+ struct Foo { a: i32,$0 }
+}
+ ",
+ "
+mod m {
+ /// `Foo` is a pretty important struct.
+ /// It does stuff.
+ #[derive($0)]
+ struct Foo { a: i32, }
+}
+ ",
+ );
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
index b8415c72a..e87132218 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -324,7 +324,7 @@ fn self_name(ast_func: &ast::Fn) -> Option<String> {
self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
}
-/// Heper function to get the name of the type of `self`
+/// Helper function to get the name of the type of `self`
fn self_type(ast_func: &ast::Fn) -> Option<ast::Type> {
ast_func.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty())
}
@@ -350,7 +350,7 @@ fn self_type_without_lifetimes(ast_func: &ast::Fn) -> Option<String> {
Some(name)
}
-/// Heper function to get the name of the type of `self` without generic arguments
+/// Helper function to get the name of the type of `self` without generic arguments
fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
let mut self_type = self_type(ast_func)?.to_string();
if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
index cd037f749..184f523e0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -192,7 +192,7 @@ fn expr_ty(
scope: &hir::SemanticsScope<'_>,
) -> Option<ast::Type> {
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
- let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
+ let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
Some(make::ty(&text))
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 076838928..c579f6780 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -196,7 +196,7 @@ fn add_func_to_accumulator(
let mut func = function_template.to_string(ctx.config.snippet_cap);
if let Some(name) = adt_name {
// FIXME: adt may have generic params.
- func = format!("\n{indent}impl {name} {{\n{func}\n{indent}}}");
+ func = format!("\n{indent}impl {} {{\n{func}\n{indent}}}", name.display(ctx.db()));
}
builder.edit_file(file);
match ctx.config.snippet_cap {
@@ -291,12 +291,9 @@ impl FunctionBuilder {
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
let is_async = await_expr.is_some();
- let (ret_type, should_focus_return_type) = make_return_type(
- ctx,
- &ast::Expr::CallExpr(call.clone()),
- target_module,
- &mut necessary_generic_params,
- );
+ let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+ let (ret_type, should_focus_return_type) =
+ make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
let (generic_param_list, where_clause) =
fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -338,12 +335,9 @@ impl FunctionBuilder {
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
let is_async = await_expr.is_some();
- let (ret_type, should_focus_return_type) = make_return_type(
- ctx,
- &ast::Expr::MethodCallExpr(call.clone()),
- target_module,
- &mut necessary_generic_params,
- );
+ let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+ let (ret_type, should_focus_return_type) =
+ make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
let (generic_param_list, where_clause) =
fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -378,6 +372,8 @@ impl FunctionBuilder {
fn_body,
self.ret_type,
self.is_async,
+ false, // FIXME : const and unsafe are not handled yet.
+ false,
);
let leading_ws;
let trailing_ws;
@@ -427,18 +423,18 @@ impl FunctionBuilder {
/// user can change the `todo!` function body.
fn make_return_type(
ctx: &AssistContext<'_>,
- call: &ast::Expr,
+ expr: &ast::Expr,
target_module: Module,
necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
) -> (Option<ast::RetType>, bool) {
let (ret_ty, should_focus_return_type) = {
- match ctx.sema.type_of_expr(call).map(TypeInfo::original) {
+ match ctx.sema.type_of_expr(expr).map(TypeInfo::original) {
Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true),
None => (Some(make::ty_placeholder()), true),
Some(ty) if ty.is_unit() => (None, false),
Some(ty) => {
necessary_generic_params.extend(ty.generic_params(ctx.db()));
- let rendered = ty.display_source_code(ctx.db(), target_module.into());
+ let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);
match rendered {
Ok(rendered) => (Some(make::ty(&rendered)), false),
Err(_) => (Some(make::ty_placeholder()), true),
@@ -893,14 +889,14 @@ fn filter_bounds_in_scope(
let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?;
let target_impl = ctx.sema.to_def(&target_impl)?;
// It's sufficient to test only the first element of `generic_params` because of the order of
- // insertion (see `relevant_parmas_and_where_clauses()`).
+ // insertion (see `params_and_where_preds_in_scope()`).
let def = generic_params.first()?.self_ty_param.parent();
if def != hir::GenericDef::Impl(target_impl) {
return None;
}
// Now we know every element that belongs to an impl would be in scope at `target`, we can
- // filter them out just by lookint at their parent.
+ // filter them out just by looking at their parent.
generic_params.retain(|it| !matches!(it.self_ty_param.parent(), hir::GenericDef::Impl(_)));
where_preds.retain(|it| {
it.node.syntax().parent().and_then(|it| it.parent()).and_then(ast::Impl::cast).is_none()
@@ -992,9 +988,9 @@ fn fn_arg_type(
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
.map(|conversion| conversion.convert_type(ctx.db()))
- .or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok())
+ .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
} else {
- ty.display_source_code(ctx.db(), target_module.into()).ok()
+ ty.display_source_code(ctx.db(), target_module.into(), true).ok()
}
}
@@ -1087,7 +1083,7 @@ fn calculate_necessary_visibility(
}
}
-// This is never intended to be used as a generic graph strucuture. If there's ever another need of
+// This is never intended to be used as a generic graph structure. If there's ever another need of
// graph algorithm, consider adding a library for that (and replace the following).
/// Minimally implemented directed graph structure represented by adjacency list.
struct Graph {
@@ -1910,7 +1906,6 @@ fn bar(new: fn) ${0:-> _} {
#[test]
fn add_function_with_closure_arg() {
- // FIXME: The argument in `bar` is wrong.
check_assist(
generate_function,
r"
@@ -1925,7 +1920,7 @@ fn foo() {
bar(closure)
}
-fn bar(closure: _) {
+fn bar(closure: impl Fn(i64) -> i64) {
${0:todo!()}
}
",
@@ -2267,13 +2262,13 @@ impl Foo {
check_assist(
generate_function,
r"
-fn foo() {
- $0bar(42).await();
+async fn foo() {
+ $0bar(42).await;
}
",
r"
-fn foo() {
- bar(42).await();
+async fn foo() {
+ bar(42).await;
}
async fn bar(arg: i32) ${0:-> _} {
@@ -2284,6 +2279,28 @@ async fn bar(arg: i32) ${0:-> _} {
}
#[test]
+ fn return_type_for_async_fn() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+async fn foo() {
+ if Err(()) = $0bar(42).await {}
+}
+",
+ r"
+async fn foo() {
+ if Err(()) = bar(42).await {}
+}
+
+async fn bar(arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+}
+",
+ );
+ }
+
+ #[test]
fn create_method() {
check_assist(
generate_function,
@@ -2381,7 +2398,7 @@ mod s {
}
#[test]
- fn create_method_with_cursor_anywhere_on_call_expresion() {
+ fn create_method_with_cursor_anywhere_on_call_expression() {
check_assist(
generate_function,
r"
@@ -2401,6 +2418,31 @@ fn foo() {S.bar();}
}
#[test]
+ fn create_async_method() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+struct S;
+async fn foo() {
+ if let Err(()) = S.$0bar(42).await {}
+}
+",
+ r"
+struct S;
+impl S {
+ async fn bar(&self, arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+ }
+}
+async fn foo() {
+ if let Err(()) = S.bar(42).await {}
+}
+",
+ )
+ }
+
+ #[test]
fn create_static_method() {
check_assist(
generate_function,
@@ -2421,6 +2463,31 @@ fn foo() {S::bar();}
}
#[test]
+ fn create_async_static_method() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+struct S;
+async fn foo() {
+ if let Err(()) = S::$0bar(42).await {}
+}
+",
+ r"
+struct S;
+impl S {
+ async fn bar(arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+ }
+}
+async fn foo() {
+ if let Err(()) = S::bar(42).await {}
+}
+",
+ )
+ }
+
+ #[test]
fn create_generic_static_method() {
check_assist(
generate_function,
@@ -2488,7 +2555,7 @@ fn foo() {s::S::bar();}
}
#[test]
- fn create_static_method_with_cursor_anywhere_on_call_expresion() {
+ fn create_static_method_with_cursor_anywhere_on_call_expression() {
check_assist(
generate_function,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
index 4595cfe29..dd6bbd84a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
@@ -174,7 +174,7 @@ pub(crate) fn generate_getter_impl(
// this buf inserts a newline at the end of a getter
// automatically, if one wants to add one more newline
// for separating it from other assoc items, that needs
- // to be handled spearately
+ // to be handled separately
let mut getter_buf =
generate_getter_from_info(ctx, &getter_info, record_field_info);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index e30a3e942..824255e4f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -98,9 +98,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
.fields()
.enumerate()
.filter_map(|(i, f)| {
- let contructor = trivial_constructors[i].clone();
- if contructor.is_some() {
- contructor
+ let constructor = trivial_constructors[i].clone();
+ if constructor.is_some() {
+ constructor
} else {
Some(f.name()?.to_string())
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 28d815e81..797180fa1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -958,7 +958,6 @@ fn main() {
);
}
- // FIXME: const generics aren't being substituted, this is blocked on better support for them
#[test]
fn inline_substitutes_generics() {
check_assist(
@@ -982,7 +981,7 @@ fn foo<T, const N: usize>() {
fn bar<U, const M: usize>() {}
fn main() {
- bar::<usize, N>();
+ bar::<usize, {0}>();
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
new file mode 100644
index 000000000..5b1540b50
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -0,0 +1,722 @@
+use syntax::{ast, AstNode};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: inline_const_as_literal
+//
+// Evaluate and inline const variable as literal.
+//
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+// STRING$0
+// }
+// ```
+// ->
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+// "Hello, World!"
+// }
+// ```
+pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let variable = ctx.find_node_at_offset::<ast::PathExpr>()?;
+
+ if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) =
+ ctx.sema.resolve_path(&variable.path()?)?
+ {
+ let konst_ty = konst.ty(ctx.sema.db);
+
+ // Used as the upper limit for recursive calls if no TCO is available
+ let fuel = 20;
+
+ // There is no way to have a const static reference to a type that contains a interior
+ // mutability cell.
+
+ // FIXME: Add support to handle type aliases for builtin scalar types.
+ validate_type_recursively(ctx, Some(&konst_ty), false, fuel)?;
+
+ let expr = konst.value(ctx.sema.db)?;
+
+ let value = match expr {
+ ast::Expr::BlockExpr(_)
+ | ast::Expr::Literal(_)
+ | ast::Expr::RefExpr(_)
+ | ast::Expr::ArrayExpr(_)
+ | ast::Expr::TupleExpr(_)
+ | ast::Expr::IfExpr(_)
+ | ast::Expr::ParenExpr(_)
+ | ast::Expr::MatchExpr(_)
+ | ast::Expr::MacroExpr(_)
+ | ast::Expr::BinExpr(_)
+ | ast::Expr::CallExpr(_) => match konst.render_eval(ctx.sema.db) {
+ Ok(result) => result,
+ Err(_) => return None,
+ },
+ _ => return None,
+ };
+
+ let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
+
+ let label = format!("Inline const as literal");
+ let target = variable.syntax().text_range();
+
+ return acc.add(id, label, target, |edit| {
+ edit.replace(variable.syntax().text_range(), value);
+ });
+ }
+ None
+}
+
+fn validate_type_recursively(
+ ctx: &AssistContext<'_>,
+ ty_hir: Option<&hir::Type>,
+ refed: bool,
+ fuel: i32,
+) -> Option<()> {
+ match (fuel > 0, ty_hir) {
+ (true, Some(ty)) if ty.is_reference() => validate_type_recursively(
+ ctx,
+ ty.as_reference().map(|(ty, _)| ty).as_ref(),
+ true,
+ // FIXME: Saving fuel when `&` repeating might not be a good idea if there's no TCO.
+ if refed { fuel } else { fuel - 1 },
+ ),
+ (true, Some(ty)) if ty.is_array() => validate_type_recursively(
+ ctx,
+ ty.as_array(ctx.db()).map(|(ty, _)| ty).as_ref(),
+ false,
+ fuel - 1,
+ ),
+ (true, Some(ty)) if ty.is_tuple() => ty
+ .tuple_fields(ctx.db())
+ .iter()
+ .all(|ty| validate_type_recursively(ctx, Some(ty), false, fuel - 1).is_some())
+ .then_some(()),
+ (true, Some(ty)) if refed && ty.is_slice() => {
+ validate_type_recursively(ctx, ty.as_slice().as_ref(), false, fuel - 1)
+ }
+ (_, Some(ty)) => match ty.as_builtin() {
+ // `const A: str` is not correct, but `const A: &builtin` is.
+ Some(builtin) if refed || (!refed && !builtin.is_str()) => Some(()),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ const NUMBER: u8 = 1;
+ const BOOL: u8 = 2;
+ const STR: u8 = 4;
+ const CHAR: u8 = 8;
+
+ const TEST_PAIRS: &[(&str, &str, u8)] = &[
+ ("u8", "0", NUMBER),
+ ("u16", "0", NUMBER),
+ ("u32", "0", NUMBER),
+ ("u64", "0", NUMBER),
+ ("u128", "0", NUMBER),
+ ("usize", "0", NUMBER),
+ ("i8", "0", NUMBER),
+ ("i16", "0", NUMBER),
+ ("i32", "0", NUMBER),
+ ("i64", "0", NUMBER),
+ ("i128", "0", NUMBER),
+ ("isize", "0", NUMBER),
+ ("bool", "false", BOOL),
+ ("&str", "\"str\"", STR),
+ ("char", "'c'", CHAR),
+ ];
+
+ // -----------Not supported-----------
+ #[test]
+ fn inline_const_as_literal_const_fn_call_slice() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> &[{ty}] {{ &[{val}] }}
+ const ABC: &[{ty}] = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit_not_applicable_const() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const STR$0ING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STRING
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_struct_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ struct A;
+ const STRUKT: A = A;
+
+ fn something() -> A {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_enum_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ enum A { A, B, C }
+ const ENUM: A = A::A;
+
+ fn something() -> A {
+ EN$0UM
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_tuple_closure() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const CLOSURE: (&dyn Fn(i32) -> i32) = (&|num| -> i32 { num });
+ fn something() -> (&dyn Fn(i32) -> i32) {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_closure_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const CLOSURE: &dyn Fn(i32) -> i32 = &|num| -> i32 { num };
+ fn something() -> &dyn Fn(i32) -> i32 {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_fn_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ struct S(i32);
+ const CON: fn(i32) -> S = S;
+ fn something() {
+ let x = CO$0N;
+ }
+ "#,
+ );
+ }
+
+ // ----------------------------
+
+ #[test]
+ fn inline_const_as_literal_const_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {val};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {val};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ {val} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ {val} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_eval_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {val} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {val} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_eval_block_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {{ {val} }} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {{ {val} }} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_block_nested_builtin() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+ const ABC: {ty} = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+ const ABC: {ty} = abc();
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_tuple() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+ const ABC: ({ty}, {ty}) = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+ const ABC: ({ty}, {ty}) = abc();
+ fn a() {{ ({val}, {val}) }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_builtin() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {val} }}
+ const ABC: {ty} = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {val} }}
+ const ABC: {ty} = abc();
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_scalar_operators() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = 1 + 2 + 3;
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = 1 + 2 + 3;
+ fn a() { 6 }
+ "#,
+ );
+ }
+ #[test]
+ fn inline_const_as_literal_block_scalar_calculate_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = { 1 + 2 + 3 };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = { 1 + 2 + 3 };
+ fn a() { 6 }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_scalar_calculate_param_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = { (1 + 2 + 3) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = { (1 + 2 + 3) };
+ fn a() { 6 }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_tuple_scalar_calculate_block_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+ fn a() { (1, 5) }
+ "#,
+ );
+ }
+
+ // FIXME: Add support for nested ref slices when using `render_eval`
+ #[test]
+ fn inline_const_as_literal_block_slice() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const ABC: &[&[&[&[&[&[i32]]]]]] = { &[&[&[&[&[&[10, 20, 30]]]]]] };
+ fn a() { A$0BC }
+ "#,
+ );
+ }
+
+ // FIXME: Add support for unary tuple expressions when using `render_eval`.
+ // `const fn abc() -> (i32) { (1) }` will results in `1` instead of `(1)` because it's evaluated
+ // as a paren expr.
+ #[test]
+ fn inline_const_as_literal_block_tuple() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+ fn a() { ([1, 2, 3], 10, (("hello", 10), 20), 30) }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_slice_single() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: [i32; 1] = { [10] };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: [i32; 1] = { [10] };
+ fn a() { [10] }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_array() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+ fn a() { [[[10]]] }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_recursive() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: &str = { { { { "hello" } } } };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: &str = { { { { "hello" } } } };
+ fn a() { "hello" }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ "Hello, World!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_block_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = {
+ let x = 9;
+ if x + 10 == 21 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+ };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = {
+ let x = 9;
+ if x + 10 == 21 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+ };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_block_macro_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = { co!() };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = { co!() };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_match_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = match 9 + 10 {
+ 0..18 => "Hello, World!",
+ _ => "World, Hello!"
+ };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = match 9 + 10 {
+ 0..18 => "Hello, World!",
+ _ => "World, Hello!"
+ };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_if_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = if 1 + 2 == 4 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = if 1 + 2 == 4 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_macro_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = co!();
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = co!();
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_call_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const fn const_call() -> &'static str {"World, Hello!"}
+ const STRING: &str = const_call();
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const fn const_call() -> &'static str {"World, Hello!"}
+ const STRING: &str = const_call();
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit_not_applicable() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STRING $0
+ }
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
index 3fc552306..5aa8e56f5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
@@ -148,7 +148,7 @@ macro_rules! num {
#[test]
fn inline_macro_simple_not_applicable_broken_macro() {
// FIXME: This is a bug. The macro should not expand, but it's
- // the same behaviour as the "Expand Macro Recursively" commmand
+ // the same behaviour as the "Expand Macro Recursively" command
// so it's presumably OK for the time being.
check_assist(
inline_macro,
@@ -254,4 +254,49 @@ fn f() { if true{}; }
"#,
)
}
+
+ #[test]
+ fn whitespace_between_text_and_pound() {
+ check_assist(
+ inline_macro,
+ r#"
+macro_rules! foo {
+ () => {
+ cfg_if! {
+ if #[cfg(test)] {
+ 1;
+ } else {
+ 1;
+ }
+ }
+ }
+}
+fn main() {
+ $0foo!();
+}
+"#,
+ r#"
+macro_rules! foo {
+ () => {
+ cfg_if! {
+ if #[cfg(test)] {
+ 1;
+ } else {
+ 1;
+ }
+ }
+ }
+}
+fn main() {
+ cfg_if!{
+ if #[cfg(test)]{
+ 1;
+ }else {
+ 1;
+ }
+};
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
index 062c816ae..b0d35c02d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -1,5 +1,5 @@
use syntax::{
- ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
+ ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams},
ted,
};
@@ -14,7 +14,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
// ```
// ->
// ```
-// fn foo<B: Bar>(bar: B) {}
+// fn foo<$0B: Bar>(bar: B) {}
// ```
pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
@@ -39,7 +39,15 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
let new_ty = make::ty(&type_param_name).clone_for_update();
ted::replace(impl_trait_type.syntax(), new_ty.syntax());
- fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
+ fn_.get_or_create_generic_param_list().add_generic_param(type_param.into());
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(generic_param) =
+ fn_.generic_param_list().and_then(|it| it.generic_params().last())
+ {
+ edit.add_tabstop_before(cap, generic_param);
+ }
+ }
},
)
}
@@ -55,7 +63,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<G>(bar: $0impl Bar) {}"#,
- r#"fn foo<G, B: Bar>(bar: B) {}"#,
+ r#"fn foo<G, $0B: Bar>(bar: B) {}"#,
);
}
@@ -64,7 +72,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo(bar: $0impl Bar) {}"#,
- r#"fn foo<B: Bar>(bar: B) {}"#,
+ r#"fn foo<$0B: Bar>(bar: B) {}"#,
);
}
@@ -73,7 +81,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
- r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
+ r#"fn foo<G, $0B: Bar>(foo: impl Foo, bar: B) {}"#,
);
}
@@ -82,7 +90,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<>(bar: $0impl Bar) {}"#,
- r#"fn foo<B: Bar>(bar: B) {}"#,
+ r#"fn foo<$0B: Bar>(bar: B) {}"#,
);
}
@@ -95,7 +103,7 @@ fn foo<
>(bar: $0impl Bar) {}
"#,
r#"
-fn foo<B: Bar
+fn foo<$0B: Bar
>(bar: B) {}
"#,
);
@@ -108,7 +116,7 @@ fn foo<B: Bar
check_assist(
introduce_named_generic,
r#"fn foo<B>(bar: $0impl Bar) {}"#,
- r#"fn foo<B, B: Bar>(bar: B) {}"#,
+ r#"fn foo<B, $0B: Bar>(bar: B) {}"#,
);
}
@@ -127,7 +135,7 @@ fn foo<
fn foo<
G: Foo,
F,
- H, B: Bar,
+ H, $0B: Bar,
>(bar: B) {}
"#,
);
@@ -138,7 +146,7 @@ fn foo<
check_assist(
introduce_named_generic,
r#"fn foo(bar: $0impl Foo + Bar) {}"#,
- r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
+ r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#,
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index a54dc4f96..c5aa9755b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -66,7 +66,7 @@ fn generate_fn_def_assist(
// if we have a self reference, use that
Some(NeedsLifetime::SelfParam(self_param))
} else {
- // otherwise, if there's a single reference parameter without a named liftime, use that
+ // otherwise, if there's a single reference parameter without a named lifetime, use that
let fn_params_without_lifetime: Vec<_> = param_list
.params()
.filter_map(|param| match param.ty() {
@@ -79,7 +79,7 @@ fn generate_fn_def_assist(
match fn_params_without_lifetime.len() {
1 => Some(fn_params_without_lifetime.into_iter().next()?),
0 => None,
- // multiple unnnamed is invalid. assist is not applicable
+ // multiple unnamed is invalid. assist is not applicable
_ => return None,
}
};
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index 641c90885..aae9f20d4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -1,4 +1,4 @@
-use hir::TypeInfo;
+use hir::Type;
use std::{collections::HashMap, iter::successors};
use syntax::{
algo::neighbor,
@@ -95,7 +95,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
}
fn are_same_types(
- current_arm_types: &HashMap<String, Option<TypeInfo>>,
+ current_arm_types: &HashMap<String, Option<Type>>,
arm: &ast::MatchArm,
ctx: &AssistContext<'_>,
) -> bool {
@@ -103,7 +103,7 @@ fn are_same_types(
for (other_arm_type_name, other_arm_type) in arm_types {
match (current_arm_types.get(&other_arm_type_name), other_arm_type) {
(Some(Some(current_arm_type)), Some(other_arm_type))
- if other_arm_type.original == current_arm_type.original => {}
+ if other_arm_type == *current_arm_type => {}
_ => return false,
}
}
@@ -114,44 +114,44 @@ fn are_same_types(
fn get_arm_types(
context: &AssistContext<'_>,
arm: &ast::MatchArm,
-) -> HashMap<String, Option<TypeInfo>> {
- let mut mapping: HashMap<String, Option<TypeInfo>> = HashMap::new();
+) -> HashMap<String, Option<Type>> {
+ let mut mapping: HashMap<String, Option<Type>> = HashMap::new();
fn recurse(
- map: &mut HashMap<String, Option<TypeInfo>>,
+ map: &mut HashMap<String, Option<Type>>,
ctx: &AssistContext<'_>,
pat: &Option<ast::Pat>,
) {
if let Some(local_pat) = pat {
- match pat {
- Some(ast::Pat::TupleStructPat(tuple)) => {
+ match local_pat {
+ ast::Pat::TupleStructPat(tuple) => {
for field in tuple.fields() {
recurse(map, ctx, &Some(field));
}
}
- Some(ast::Pat::TuplePat(tuple)) => {
+ ast::Pat::TuplePat(tuple) => {
for field in tuple.fields() {
recurse(map, ctx, &Some(field));
}
}
- Some(ast::Pat::RecordPat(record)) => {
+ ast::Pat::RecordPat(record) => {
if let Some(field_list) = record.record_pat_field_list() {
for field in field_list.fields() {
recurse(map, ctx, &field.pat());
}
}
}
- Some(ast::Pat::ParenPat(parentheses)) => {
+ ast::Pat::ParenPat(parentheses) => {
recurse(map, ctx, &parentheses.pat());
}
- Some(ast::Pat::SlicePat(slice)) => {
+ ast::Pat::SlicePat(slice) => {
for slice_pat in slice.pats() {
recurse(map, ctx, &Some(slice_pat));
}
}
- Some(ast::Pat::IdentPat(ident_pat)) => {
+ ast::Pat::IdentPat(ident_pat) => {
if let Some(name) = ident_pat.name() {
- let pat_type = ctx.sema.type_of_pat(local_pat);
+ let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat);
map.insert(name.text().to_string(), pat_type);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
index d848fce4b..b6027eac5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -98,7 +98,7 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
};
builder.delete(range_to_delete);
- let const_ref = format!("Self::{name}");
+ let const_ref = format!("Self::{}", name.display(ctx.db()));
for range in usages.all().file_ranges().map(|it| it.range) {
builder.replace(range, const_ref.clone());
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index 1728c03cd..917d0b367 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let target = source_file.syntax().text_range();
- let module_name = module.name(ctx.db())?.to_string();
+ let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
let path = format!("../{module_name}.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
index a7c605325..166b25c69 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let mut buf = String::from("./");
match parent_module.name(ctx.db()) {
Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
- format_to!(buf, "{name}/")
+ format_to!(buf, "{}/", name.display(ctx.db()))
}
_ => (),
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index 076d25411..b73270cd0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
let target = source_file.syntax().text_range();
- let module_name = module.name(ctx.db())?.to_string();
+ let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
let path = format!("./{module_name}/mod.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index cbbea6c1e..23153b4c5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
let local = ctx.sema.to_def(&pat)?;
let ty = ctx.sema.type_of_pat(&pat.into())?.original;
- if ty.contains_unknown() || ty.is_closure() {
- cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred);
- return None;
- }
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
+ Ok(ty) => ty,
+ Err(_) => {
+ cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
+ return None;
+ }
+ };
let initializer = let_stmt.initializer()?;
if !is_body_const(&ctx.sema, &initializer) {
@@ -187,7 +189,7 @@ fn foo() {
#[test]
fn not_applicable_unknown_ty() {
- cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred);
+ cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
check_assist_not_applicable(
promote_local_to_const,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 4cfe6c99b..a5c7fea40 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -386,7 +386,7 @@ fn foo() {
}
#[test]
- fn pull_assignment_up_if_missing_assigment_not_applicable() {
+ fn pull_assignment_up_if_missing_assignment_not_applicable() {
check_assist_not_applicable(
pull_assignment_up,
r#"
@@ -401,7 +401,7 @@ fn foo() {
}
#[test]
- fn pull_assignment_up_match_missing_assigment_not_applicable() {
+ fn pull_assignment_up_match_missing_assignment_not_applicable() {
check_assist_not_applicable(
pull_assignment_up,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index e7014597a..4bf974a56 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -507,7 +507,7 @@ fn main() {
}
#[test]
- fn struct_method_over_stuct_instance() {
+ fn struct_method_over_struct_instance() {
check_assist_not_applicable(
qualify_method_call,
r#"
@@ -525,7 +525,7 @@ fn main() {
}
#[test]
- fn trait_method_over_stuct_instance() {
+ fn trait_method_over_struct_instance() {
check_assist_not_applicable(
qualify_method_call,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index e759e1561..239149dc4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -86,7 +86,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
acc.add_group(
&group_label,
AssistId("qualify_path", AssistKind::QuickFix),
- label(candidate, &import),
+ label(ctx.db(), candidate, &import),
range,
|builder| {
qualify_candidate.qualify(
@@ -186,7 +186,7 @@ fn find_trait_method(
if let Some(hir::AssocItem::Function(method)) =
trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
item.name(db)
- .map(|name| name.to_string() == trait_method_name.to_string())
+ .map(|name| name.display(db).to_string() == trait_method_name.to_string())
.unwrap_or(false)
})
{
@@ -216,14 +216,14 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
GroupLabel(format!("Qualify {name}"))
}
-fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
+fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
let import_path = &import.import_path;
match candidate {
ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
- format!("Qualify as `{import_path}`")
+ format!("Qualify as `{}`", import_path.display(db))
}
- _ => format!("Qualify with `{import_path}`"),
+ _ => format!("Qualify with `{}`", import_path.display(db)),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
index 01420430b..63db60633 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
@@ -20,6 +20,7 @@ use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists
// }
// ```
pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ // FIXME: This should support byte and c strings as well.
let token = ctx.find_token_at_offset::<ast::String>()?;
if token.is_raw() {
return None;
@@ -157,9 +158,8 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
-
use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
#[test]
fn make_raw_string_target() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
index e9c7c6bae..ffc32f804 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -124,7 +124,7 @@ mod tests {
}
#[test]
- fn remove_parens_doesnt_apply_weird_syntax_and_adge_cases() {
+ fn remove_parens_doesnt_apply_weird_syntax_and_edge_cases() {
// removing `()` would break code because {} would be counted as the loop/if body
check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(0..{3}) {} }"#);
check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(S {}) {} }"#);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
index bd2e8fbe3..0772b168d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -232,7 +232,7 @@ fn b() { foo( ) }
}
#[test]
- fn remove_unused_surrounded_by_parms() {
+ fn remove_unused_surrounded_by_params() {
check_assist(
remove_unused_param,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
index 58dcaf9a2..025625669 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -20,9 +20,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// const test: Foo = Foo {foo: 1, bar: 0}
// ```
pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let record = ctx.find_node_at_offset::<Either<ast::RecordExpr, ast::RecordPat>>()?;
+ let path = ctx.find_node_at_offset::<ast::Path>()?;
+ let record =
+ path.syntax().parent().and_then(<Either<ast::RecordExpr, ast::RecordPat>>::cast)?;
- let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
let ranks = compute_fields_ranks(&path, ctx)?;
let get_rank_of_field =
|of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
@@ -96,7 +97,7 @@ fn compute_fields_ranks(
.fields(ctx.db())
.into_iter()
.enumerate()
- .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
+ .map(|(idx, field)| (field.name(ctx.db()).display(ctx.db()).to_string(), idx))
.collect();
Some(res)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
index 208c3e109..666696623 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// }
//
// struct Bar;
-// $0impl Foo for Bar {
+// $0impl Foo for Bar$0 {
// const B: u8 = 17;
// fn c() {}
// type A = String;
@@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
let items = impl_ast.assoc_item_list()?;
+
+ // restrict the range
+ // if cursor is in assoc_items, abort
+ let assoc_range = items.syntax().text_range();
+ let cursor_position = ctx.offset();
+ if assoc_range.contains_inclusive(cursor_position) {
+ cov_mark::hit!(not_applicable_editing_assoc_items);
+ return None;
+ }
+
let assoc_items = items.assoc_items().collect::<Vec<_>>();
let path = impl_ast
@@ -104,7 +114,7 @@ fn compute_item_ranks(
.iter()
.flat_map(|i| i.name(ctx.db()))
.enumerate()
- .map(|(idx, name)| (name.to_string(), idx))
+ .map(|(idx, name)| (name.display(ctx.db()).to_string(), idx))
.collect(),
)
}
@@ -264,9 +274,9 @@ trait Bar {
}
struct Foo;
-impl Bar for Foo {
+$0impl Bar for Foo {
type Fooo = ();
- type Foo = ();$0
+ type Foo = ();
}"#,
r#"
trait Bar {
@@ -281,4 +291,29 @@ impl Bar for Foo {
}"#,
)
}
+
+ #[test]
+ fn not_applicable_editing_assoc_items() {
+ cov_mark::check!(not_applicable_editing_assoc_items);
+ check_assist_not_applicable(
+ reorder_impl_items,
+ r#"
+trait Bar {
+ type T;
+ const C: ();
+ fn a() {}
+ fn z() {}
+ fn b() {}
+}
+struct Foo;
+impl Bar for Foo {
+ type T = ();$0
+ const C: () = ();
+ fn z() {}
+ fn a() {}
+ fn b() {}
+}
+ "#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 4cfae0c72..3bdd795be 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,8 +1,5 @@
use hir::{InFile, ModuleDef};
-use ide_db::{
- helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator,
- syntax_helpers::insert_whitespace_into_node::insert_ws_into,
-};
+use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
use itertools::Itertools;
use syntax::{
ast::{self, AstNode, HasName},
@@ -59,7 +56,7 @@ pub(crate) fn replace_derive_with_manual_impl(
// collect the derive paths from the #[derive] expansion
let current_derives = ctx
.sema
- .parse_or_expand(hir_file)?
+ .parse_or_expand(hir_file)
.descendants()
.filter_map(ast::Attr::cast)
.filter_map(|attr| attr.path())
@@ -182,7 +179,11 @@ fn impl_def_from_trait(
let impl_def = {
use syntax::ast::Impl;
let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
- let parse = syntax::SourceFile::parse(&text);
+ // FIXME: `generate_trait_impl_text` currently generates two newlines
+ // at the front, but these leading newlines should really instead be
+ // inserted at the same time the impl is inserted
+ assert_eq!(&text[..2], "\n\n", "`generate_trait_impl_text` output changed");
+ let parse = syntax::SourceFile::parse(&text[2..]);
let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
Some(it) => it,
None => {
@@ -193,24 +194,13 @@ fn impl_def_from_trait(
)
}
};
- let node = node.clone_subtree();
+ let node = node.clone_for_update();
assert_eq!(node.syntax().text_range().start(), 0.into());
node
};
- let trait_items = trait_items
- .into_iter()
- .map(|it| {
- if sema.hir_file_for(it.syntax()).is_macro() {
- if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
- return it;
- }
- }
- it.clone_for_update()
- })
- .collect();
- let (impl_def, first_assoc_item) =
- add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
+ let first_assoc_item =
+ add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
// Generate a default `impl` function body for the derived trait.
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
new file mode 100644
index 000000000..e7b62d49b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -0,0 +1,351 @@
+use hir::Semantics;
+use ide_db::{
+ base_db::{FileId, FileRange},
+ defs::Definition,
+ search::{SearchScope, UsageSearchResult},
+ RootDatabase,
+};
+use syntax::{
+ ast::{
+ self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds, Name, NameLike,
+ PathType,
+ },
+ match_ast, ted, AstNode,
+};
+use text_edit::TextRange;
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: replace_named_generic_with_impl
+//
+// Replaces named generic with an `impl Trait` in function argument.
+//
+// ```
+// fn new<P$0: AsRef<Path>>(location: P) -> Self {}
+// ```
+// ->
+// ```
+// fn new(location: impl AsRef<Path>) -> Self {}
+// ```
+pub(crate) fn replace_named_generic_with_impl(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ // finds `<P: AsRef<Path>>`
+ let type_param = ctx.find_node_at_offset::<ast::TypeParam>()?;
+ // returns `P`
+ let type_param_name = type_param.name()?;
+
+ // The list of type bounds / traits: `AsRef<Path>`
+ let type_bound_list = type_param.type_bound_list()?;
+
+ let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?;
+ let param_list_text_range = fn_.param_list()?.syntax().text_range();
+
+ let type_param_hir_def = ctx.sema.to_def(&type_param)?;
+ let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param_hir_def));
+
+ // get all usage references for the type param
+ let usage_refs = find_usages(&ctx.sema, &fn_, type_param_def, ctx.file_id());
+ if usage_refs.is_empty() {
+ return None;
+ }
+
+ // All usage references need to be valid (inside the function param list)
+ if !check_valid_usages(&usage_refs, param_list_text_range) {
+ return None;
+ }
+
+ let mut path_types_to_replace = Vec::new();
+ for (_a, refs) in usage_refs.iter() {
+ for usage_ref in refs {
+ let param_node = find_path_type(&ctx.sema, &type_param_name, &usage_ref.name)?;
+ path_types_to_replace.push(param_node);
+ }
+ }
+
+ let target = type_param.syntax().text_range();
+
+ acc.add(
+ AssistId("replace_named_generic_with_impl", AssistKind::RefactorRewrite),
+ "Replace named generic with impl trait",
+ target,
+ |edit| {
+ let type_param = edit.make_mut(type_param);
+ let fn_ = edit.make_mut(fn_);
+
+ let path_types_to_replace = path_types_to_replace
+ .into_iter()
+ .map(|param| edit.make_mut(param))
+ .collect::<Vec<_>>();
+
+ // remove trait from generic param list
+ if let Some(generic_params) = fn_.generic_param_list() {
+ generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param));
+ if generic_params.generic_params().count() == 0 {
+ ted::remove(generic_params.syntax());
+ }
+ }
+
+ let new_bounds = impl_trait_type(type_bound_list);
+ for path_type in path_types_to_replace.iter().rev() {
+ ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax());
+ }
+ },
+ )
+}
+
+fn find_path_type(
+ sema: &Semantics<'_, RootDatabase>,
+ type_param_name: &Name,
+ param: &NameLike,
+) -> Option<PathType> {
+ let path_type =
+ sema.ancestors_with_macros(param.syntax().clone()).find_map(ast::PathType::cast)?;
+
+ // Ignore any path types that look like `P::Assoc`
+ if path_type.path()?.as_single_name_ref()?.text() != type_param_name.text() {
+ return None;
+ }
+
+ let ancestors = sema.ancestors_with_macros(path_type.syntax().clone());
+
+ let mut in_generic_arg_list = false;
+ let mut is_associated_type = false;
+
+ // walking the ancestors checks them in a heuristic way until the `Fn` node is reached.
+ for ancestor in ancestors {
+ match_ast! {
+ match ancestor {
+ ast::PathSegment(ps) => {
+ match ps.kind()? {
+ ast::PathSegmentKind::Name(_name_ref) => (),
+ ast::PathSegmentKind::Type { .. } => return None,
+ _ => return None,
+ }
+ },
+ ast::GenericArgList(_) => {
+ in_generic_arg_list = true;
+ },
+ ast::AssocTypeArg(_) => {
+ is_associated_type = true;
+ },
+ ast::ImplTraitType(_) => {
+ if in_generic_arg_list && !is_associated_type {
+ return None;
+ }
+ },
+ ast::DynTraitType(_) => {
+ if !is_associated_type {
+ return None;
+ }
+ },
+ ast::Fn(_) => return Some(path_type),
+ _ => (),
+ }
+ }
+ }
+
+ None
+}
+
+/// Returns all usage references for the given type parameter definition.
+fn find_usages(
+ sema: &Semantics<'_, RootDatabase>,
+ fn_: &ast::Fn,
+ type_param_def: Definition,
+ file_id: FileId,
+) -> UsageSearchResult {
+ let file_range = FileRange { file_id, range: fn_.syntax().text_range() };
+ type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all()
+}
+
+fn check_valid_usages(usages: &UsageSearchResult, param_list_range: TextRange) -> bool {
+ usages
+ .iter()
+ .flat_map(|(_, usage_refs)| usage_refs)
+ .all(|usage_ref| param_list_range.contains_range(usage_ref.range))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ #[test]
+ fn replace_generic_moves_into_function() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString>(input: T) -> Self {}"#,
+ r#"fn new(input: impl ToString) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_inner_associated_type() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: AsRef<Path>>(input: P) -> Self {}"#,
+ r#"fn new(input: impl AsRef<Path>) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_trait_applies_to_all_matching_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString>(a: T, b: T) -> Self {}"#,
+ r#"fn new(a: impl ToString, b: impl ToString) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_trait_applies_to_generic_arguments_in_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"
+ fn foo<P$0: Trait>(
+ _: P,
+ _: Option<P>,
+ _: Option<Option<P>>,
+ _: impl Iterator<Item = P>,
+ _: &dyn Iterator<Item = P>,
+ ) {}
+ "#,
+ r#"
+ fn foo(
+ _: impl Trait,
+ _: Option<impl Trait>,
+ _: Option<Option<impl Trait>>,
+ _: impl Iterator<Item = impl Trait>,
+ _: &dyn Iterator<Item = impl Trait>,
+ ) {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_one_param_type_is_invalid() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"
+ fn foo<P$0: Trait>(
+ _: i32,
+ _: Option<P>,
+ _: Option<Option<P>>,
+ _: impl Iterator<Item = P>,
+ _: &dyn Iterator<Item = P>,
+ _: <P as Trait>::Assoc,
+ ) {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_referenced_in_where_clause() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait, I>() where I: FromRef<P> {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_with_type_alias() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(p: <P as Trait>::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_as_argument_in_outer_trait_alias() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: <() as OtherTrait<P>>::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_with_inner_associated_type() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: P::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_passed_into_outer_impl_trait() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: impl OtherTrait<P>) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_in_passed_function_parameter() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: &dyn Fn(P)) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_multiple_generic_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P: AsRef<Path>, T$0: ToString>(t: T, p: P) -> Self {}"#,
+ r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"#,
+ );
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString, P: AsRef<Path>>(t: T, p: P) -> Self {}"#,
+ r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"#,
+ );
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<A: Send, B$0: ToString, C: Debug>(a: A, b: B, c: C) -> Self {}"#,
+ r#"fn new<A: Send, C: Debug>(a: A, b: impl ToString, c: C) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_multiple_trait_bounds() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: Send + Sync>(p: P) -> Self {}"#,
+ r#"fn new(p: impl Send + Sync) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_if_param_used_as_return_type() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: Send + Sync>(p: P) -> P {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_if_param_used_in_fn_body() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: ToString>(p: P) { let x: &dyn P = &O; }"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_ignores_another_function_with_same_param_type() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"
+ fn new<P$0: Send + Sync>(p: P) {}
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
+ "#,
+ r#"
+ fn new(p: impl Send + Sync) {}
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
index decb5fb62..6310981cc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
@@ -31,14 +31,14 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_
if value.chars().take(2).count() != 1 {
return None;
}
- let quote_offets = token.quote_offsets()?;
+ let quote_offsets = token.quote_offsets()?;
acc.add(
AssistId("replace_string_with_char", AssistKind::RefactorRewrite),
"Replace string with char",
target,
|edit| {
- let (left, right) = quote_offets.quotes;
+ let (left, right) = quote_offsets.quotes;
edit.replace(left, '\'');
edit.replace(right, '\'');
if value == "'" {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
index 38fccb338..2e26f59d0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
@@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
// Replaces a `try` expression with a `match` expression.
//
// ```
-// # //- minicore:option
+// # //- minicore: try, option
// fn handle() {
// let pat = Some(true)$0?;
// }
@@ -111,7 +111,7 @@ mod tests {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:option
+//- minicore: try, option
fn test() {
let pat = Some(true)$0?;
}
@@ -132,7 +132,7 @@ fn test() {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:result
+//- minicore: try, from, result
fn test() {
let pat = Ok(true)$0?;
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 6626ce079..43a97d7d3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
let returned_type = match ctx.sema.type_of_expr(&initializer) {
Some(returned_type) if !returned_type.original.contains_unknown() => {
let module = ctx.sema.scope(let_stmt.syntax())?.module();
- returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
+ returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()?
}
_ => {
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
index a93704b39..3a0121f55 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
@@ -87,11 +87,7 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
return None;
}
- if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
- add_sort_methods_assist(acc, trait_ast.assoc_item_list()?)
- } else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
- add_sort_methods_assist(acc, impl_ast.assoc_item_list()?)
- } else if let Some(struct_ast) = ctx.find_node_at_offset::<ast::Struct>() {
+ if let Some(struct_ast) = ctx.find_node_at_offset::<ast::Struct>() {
add_sort_field_list_assist(acc, struct_ast.field_list())
} else if let Some(union_ast) = ctx.find_node_at_offset::<ast::Union>() {
add_sort_fields_assist(acc, union_ast.record_field_list()?)
@@ -103,6 +99,10 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
add_sort_fields_assist(acc, enum_struct_variant_ast)
} else if let Some(enum_ast) = ctx.find_node_at_offset::<ast::Enum>() {
add_sort_variants_assist(acc, enum_ast.variant_list()?)
+ } else if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
+ add_sort_methods_assist(acc, ctx, trait_ast.assoc_item_list()?)
+ } else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
+ add_sort_methods_assist(acc, ctx, impl_ast.assoc_item_list()?)
} else {
None
}
@@ -116,9 +116,11 @@ trait AddRewrite {
new: Vec<T>,
target: TextRange,
) -> Option<()>;
+ fn yeet() {}
}
impl AddRewrite for Assists {
+ fn yeet() {}
fn add_rewrite<T: AstNode>(
&mut self,
label: &str,
@@ -146,7 +148,19 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option<ast::FieldLi
}
}
-fn add_sort_methods_assist(acc: &mut Assists, item_list: ast::AssocItemList) -> Option<()> {
+fn add_sort_methods_assist(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+ item_list: ast::AssocItemList,
+) -> Option<()> {
+ let selection = ctx.selection_trimmed();
+
+ // ignore assist if the selection intersects with an associated item.
+ if item_list.assoc_items().any(|item| item.syntax().text_range().intersect(selection).is_some())
+ {
+ return None;
+ }
+
let methods = get_methods(&item_list);
let sorted = sort_by_name(&methods);
@@ -217,6 +231,51 @@ mod tests {
use super::*;
#[test]
+ fn not_applicable_if_selection_in_fn_body() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl S {
+ fn func2() {
+ $0 bar $0
+ }
+ fn func() {}
+}
+ "#,
+ )
+ }
+
+ #[test]
+ fn not_applicable_if_selection_at_associated_const() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl S {
+ fn func2() {}
+ fn func() {}
+ const C: () = $0()$0;
+}
+ "#,
+ )
+ }
+
+ #[test]
+ fn not_applicable_if_selection_overlaps_nodes() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl $0S {
+ fn$0 func2() {}
+ fn func() {}
+}
+ "#,
+ )
+ }
+
+ #[test]
fn not_applicable_if_no_selection() {
cov_mark::check!(not_applicable_if_no_selection);
@@ -232,6 +291,21 @@ t$0rait Bar {
}
#[test]
+ fn not_applicable_if_selection_in_trait_fn_body() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+trait Bar {
+ fn b() {
+ $0 hello $0
+ }
+ fn a();
+}
+ "#,
+ )
+ }
+
+ #[test]
fn not_applicable_if_trait_empty() {
cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
@@ -459,6 +533,31 @@ struct Bar {
}
#[test]
+ fn sort_struct_inside_a_function() {
+ check_assist(
+ sort_items,
+ r#"
+fn hello() {
+ $0struct Bar$0 {
+ b: u8,
+ a: u32,
+ c: u64,
+ }
+}
+ "#,
+ r#"
+fn hello() {
+ struct Bar {
+ a: u32,
+ b: u8,
+ c: u64,
+ }
+}
+ "#,
+ )
+ }
+
+ #[test]
fn sort_generic_struct_with_lifetime() {
check_assist(
sort_items,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index 33b19a354..939055f14 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -51,15 +51,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let replaced = match list.syntax().last_child() {
Some(last) => {
let stmts: Vec<ast::Stmt> = list.statements().collect();
- let initializer = ast::Expr::cast(last.clone())?;
+ let initializer = ast::Expr::cast(last)?;
let let_stmt = make::let_stmt(pattern, ty, Some(initializer));
if stmts.len() > 0 {
let block = make::block_expr(stmts, None);
- format!(
- "{}\n {}",
- update_expr_string(block.to_string()),
- let_stmt.to_string()
- )
+ format!("{}\n {}", update_expr_string(block.to_string()), let_stmt)
} else {
let_stmt.to_string()
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
index 9ef4ae047..26f3c1926 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
@@ -5,7 +5,7 @@ use ide_db::{
use itertools::Itertools;
use syntax::{
ast::{self, Expr},
- match_ast, AstNode, TextRange, TextSize,
+ match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -38,14 +38,15 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<'
};
let type_ref = &ret_type.ty()?;
- let ty = ctx.sema.resolve_type(type_ref)?.as_adt();
+ let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { return None; };
let result_enum =
FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?;
-
- if !matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) {
+ if ret_enum != result_enum {
return None;
}
+ let Some(ok_type) = unwrap_result_type(type_ref) else { return None; };
+
acc.add(
AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite),
"Unwrap Result return type",
@@ -64,26 +65,19 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<'
});
for_each_tail_expr(&body, tail_cb);
- let mut is_unit_type = false;
- if let Some((_, inner_type)) = type_ref.to_string().split_once('<') {
- let inner_type = match inner_type.split_once(',') {
- Some((success_inner_type, _)) => success_inner_type,
- None => inner_type,
- };
- let new_ret_type = inner_type.strip_suffix('>').unwrap_or(inner_type);
- if new_ret_type == "()" {
- is_unit_type = true;
- let text_range = TextRange::new(
- ret_type.syntax().text_range().start(),
- ret_type.syntax().text_range().end() + TextSize::from(1u32),
- );
- builder.delete(text_range)
- } else {
- builder.replace(
- type_ref.syntax().text_range(),
- inner_type.strip_suffix('>').unwrap_or(inner_type),
- )
+ let is_unit_type = is_unit_type(&ok_type);
+ if is_unit_type {
+ let mut text_range = ret_type.syntax().text_range();
+
+ if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() {
+ if token.kind() == SyntaxKind::WHITESPACE {
+ text_range = TextRange::new(text_range.start(), token.text_range().end());
+ }
}
+
+ builder.delete(text_range);
+ } else {
+ builder.replace(type_ref.syntax().text_range(), ok_type.syntax().text());
}
for ret_expr_arg in exprs_to_unwrap {
@@ -134,6 +128,22 @@ fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
}
}
+// Tries to extract `T` from `Result<T, E>`.
+fn unwrap_result_type(ty: &ast::Type) -> Option<ast::Type> {
+ let ast::Type::PathType(path_ty) = ty else { return None; };
+ let path = path_ty.path()?;
+ let segment = path.first_segment()?;
+ let generic_arg_list = segment.generic_arg_list()?;
+ let generic_args: Vec<_> = generic_arg_list.generic_args().collect();
+ let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { return None; };
+ ok_type.ty()
+}
+
+fn is_unit_type(ty: &ast::Type) -> bool {
+ let ast::Type::TupleType(tuple) = ty else { return false };
+ tuple.fields().next().is_none()
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -175,6 +185,21 @@ fn foo() {
}
"#,
);
+
+ // Unformatted return type
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result
+fn foo() -> Result<(), Box<dyn Error$0>>{
+ Ok(())
+}
+"#,
+ r#"
+fn foo() {
+}
+"#,
+ );
}
#[test]
@@ -1017,4 +1042,52 @@ fn foo(the_field: u32) -> u32 {
"#,
);
}
+
+ #[test]
+ fn unwrap_result_return_type_nested_type() {
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option
+fn foo() -> Result<Option<i32$0>, ()> {
+ Ok(Some(42))
+}
+"#,
+ r#"
+fn foo() -> Option<i32> {
+ Some(42)
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option
+fn foo() -> Result<Option<Result<i32$0, ()>>, ()> {
+ Ok(None)
+}
+"#,
+ r#"
+fn foo() -> Option<Result<i32, ()>> {
+ None
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option, iterators
+fn foo() -> Result<impl Iterator<Item = i32>$0, ()> {
+ Ok(Some(42).into_iter())
+}
+"#,
+ r#"
+fn foo() -> impl Iterator<Item = i32> {
+ Some(42).into_iter()
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 8b07e29a5..111753bf3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -122,6 +122,7 @@ mod handlers {
mod convert_iter_for_each_to_for;
mod convert_let_else_to_match;
mod convert_match_to_let_else;
+ mod convert_nested_function_to_closure;
mod convert_tuple_struct_to_named_struct;
mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return;
@@ -160,6 +161,7 @@ mod handlers {
mod generate_delegate_methods;
mod add_return_type;
mod inline_call;
+ mod inline_const_as_literal;
mod inline_local_variable;
mod inline_macro;
mod inline_type_alias;
@@ -192,6 +194,7 @@ mod handlers {
mod replace_arith_op;
mod introduce_named_generic;
mod replace_let_with_if_let;
+ mod replace_named_generic_with_impl;
mod replace_qualified_name_with_use;
mod replace_string_with_char;
mod replace_turbofish_with_explicit_type;
@@ -228,8 +231,9 @@ mod handlers {
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match,
- convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
convert_match_to_let_else::convert_match_to_let_else,
+ convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
+ convert_nested_function_to_closure::convert_nested_function_to_closure,
convert_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
@@ -262,6 +266,7 @@ mod handlers {
generate_new::generate_new,
inline_call::inline_call,
inline_call::inline_into_callers,
+ inline_const_as_literal::inline_const_as_literal,
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
@@ -297,6 +302,7 @@ mod handlers {
replace_let_with_if_let::replace_let_with_if_let,
replace_method_eager_lazy::replace_with_eager_method,
replace_method_eager_lazy::replace_with_lazy_method,
+ replace_named_generic_with_impl::replace_named_generic_with_impl,
replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
replace_qualified_name_with_use::replace_qualified_name_with_use,
replace_arith_op::replace_arith_with_wrapping,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 94be99fd7..344f2bfcc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -3,7 +3,7 @@ mod generated;
mod sourcegen;
use expect_test::expect;
-use hir::{db::DefDatabase, Semantics};
+use hir::Semantics;
use ide_db::{
base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -161,7 +161,7 @@ fn check_with_config(
assist_label: Option<&str>,
) {
let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
- db.set_enable_proc_attr_macros(true);
+ db.enable_proc_attr_macros();
let text_without_caret = db.file_text(file_with_caret_id).to_string();
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
@@ -273,8 +273,9 @@ fn assist_order_field_struct() {
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
- assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct");
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
+ assert_eq!(assists.next().expect("expected assist").label, "Generate `new`");
+ assert_eq!(assists.next().map(|it| it.label.to_string()), None);
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index e5a8d675a..c097e0739 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -439,7 +439,7 @@ fn doctest_convert_match_to_let_else() {
r#####"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -495,6 +495,31 @@ impl Point {
}
#[test]
+fn doctest_convert_nested_function_to_closure() {
+ check_doc_test(
+ "convert_nested_function_to_closure",
+ r#####"
+fn main() {
+ fn fo$0o(label: &str, number: u64) {
+ println!("{}: {}", label, number);
+ }
+
+ foo("Bar", 100);
+}
+"#####,
+ r#####"
+fn main() {
+ let foo = |label: &str, number: u64| {
+ println!("{}: {}", label, number);
+ };
+
+ foo("Bar", 100);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_convert_to_guarded_return() {
check_doc_test(
"convert_to_guarded_return",
@@ -1455,6 +1480,27 @@ fn foo(name: Option<&str>) {
}
#[test]
+fn doctest_inline_const_as_literal() {
+ check_doc_test(
+ "inline_const_as_literal",
+ r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+ STRING$0
+}
+"#####,
+ r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+ "Hello, World!"
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_inline_into_callers() {
check_doc_test(
"inline_into_callers",
@@ -1596,7 +1642,7 @@ fn doctest_introduce_named_generic() {
fn foo(bar: $0impl Bar) {}
"#####,
r#####"
-fn foo<B: Bar>(bar: B) {}
+fn foo<$0B: Bar>(bar: B) {}
"#####,
)
}
@@ -2116,7 +2162,7 @@ trait Foo {
}
struct Bar;
-$0impl Foo for Bar {
+$0impl Foo for Bar$0 {
const B: u8 = 17;
fn c() {}
type A = String;
@@ -2314,6 +2360,19 @@ fn handle(action: Action) {
}
#[test]
+fn doctest_replace_named_generic_with_impl() {
+ check_doc_test(
+ "replace_named_generic_with_impl",
+ r#####"
+fn new<P$0: AsRef<Path>>(location: P) -> Self {}
+"#####,
+ r#####"
+fn new(location: impl AsRef<Path>) -> Self {}
+"#####,
+ )
+}
+
+#[test]
fn doctest_replace_qualified_name_with_use() {
check_doc_test(
"replace_qualified_name_with_use",
@@ -2352,7 +2411,7 @@ fn doctest_replace_try_expr_with_match() {
check_doc_test(
"replace_try_expr_with_match",
r#####"
-//- minicore:option
+//- minicore: try, option
fn handle() {
let pat = Some(true)$0?;
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
index b4f50c7fb..3da90e905 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
@@ -90,8 +90,6 @@ impl Assist {
let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text);
for block in comment_blocks {
- // FIXME: doesn't support blank lines yet, need to tweak
- // `extract_comment_blocks` for that.
let id = block.id;
assert!(
id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index f323ebcf7..03d855350 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -3,14 +3,17 @@
use std::ops;
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
-use hir::{db::HirDatabase, HirDisplay, Semantics};
-use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
+use hir::{db::HirDatabase, HirDisplay, InFile, Semantics};
+use ide_db::{
+ famous_defs::FamousDefs, path_transform::PathTransform,
+ syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, SnippetCap,
+};
use stdx::format_to;
use syntax::{
ast::{
self,
- edit::{self, AstNodeEdit},
- edit_in_place::{AttrsOwnerEdit, Removable},
+ edit::{AstNodeEdit, IndentLevel},
+ edit_in_place::{AttrsOwnerEdit, Indent, Removable},
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
},
ted, AstNode, AstToken, Direction, SourceFile,
@@ -91,30 +94,21 @@ pub fn filter_assoc_items(
sema: &Semantics<'_, RootDatabase>,
items: &[hir::AssocItem],
default_methods: DefaultMethods,
-) -> Vec<ast::AssocItem> {
- fn has_def_name(item: &ast::AssocItem) -> bool {
- match item {
- ast::AssocItem::Fn(def) => def.name(),
- ast::AssocItem::TypeAlias(def) => def.name(),
- ast::AssocItem::Const(def) => def.name(),
- ast::AssocItem::MacroCall(_) => None,
- }
- .is_some()
- }
-
- items
+) -> Vec<InFile<ast::AssocItem>> {
+ return items
.iter()
// Note: This throws away items with no source.
- .filter_map(|&i| {
- let item = match i {
- hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
- hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
- hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
+ .copied()
+ .filter_map(|assoc_item| {
+ let item = match assoc_item {
+ hir::AssocItem::Function(it) => sema.source(it)?.map(ast::AssocItem::Fn),
+ hir::AssocItem::TypeAlias(it) => sema.source(it)?.map(ast::AssocItem::TypeAlias),
+ hir::AssocItem::Const(it) => sema.source(it)?.map(ast::AssocItem::Const),
};
Some(item)
})
.filter(has_def_name)
- .filter(|it| match it {
+ .filter(|it| match &it.value {
ast::AssocItem::Fn(def) => matches!(
(default_methods, def.body()),
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
@@ -125,36 +119,67 @@ pub fn filter_assoc_items(
),
_ => default_methods == DefaultMethods::No,
})
- .collect::<Vec<_>>()
+ .collect();
+
+ fn has_def_name(item: &InFile<ast::AssocItem>) -> bool {
+ match &item.value {
+ ast::AssocItem::Fn(def) => def.name(),
+ ast::AssocItem::TypeAlias(def) => def.name(),
+ ast::AssocItem::Const(def) => def.name(),
+ ast::AssocItem::MacroCall(_) => None,
+ }
+ .is_some()
+ }
}
+/// Given `original_items` retrieved from the trait definition (usually by
+/// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it,
+/// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got
+/// inserted.
pub fn add_trait_assoc_items_to_impl(
sema: &Semantics<'_, RootDatabase>,
- items: Vec<ast::AssocItem>,
+ original_items: &[InFile<ast::AssocItem>],
trait_: hir::Trait,
- impl_: ast::Impl,
+ impl_: &ast::Impl,
target_scope: hir::SemanticsScope<'_>,
-) -> (ast::Impl, ast::AssocItem) {
- let source_scope = sema.scope_for_def(trait_);
-
- let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+) -> ast::AssocItem {
+ let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
+ let items = original_items.into_iter().map(|InFile { file_id, value: original_item }| {
+ let cloned_item = {
+ if file_id.is_macro() {
+ if let Some(formatted) =
+ ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone()))
+ {
+ return formatted;
+ } else {
+ stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
+ }
+ }
+ original_item.clone_for_update()
+ };
- let items = items.into_iter().map(|assoc_item| {
- transform.apply(assoc_item.syntax());
- assoc_item.remove_attrs_and_docs();
- assoc_item
+ if let Some(source_scope) = sema.scope(original_item.syntax()) {
+ // FIXME: Paths in nested macros are not handled well. See
+ // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test.
+ let transform =
+ PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+ transform.apply(cloned_item.syntax());
+ }
+ cloned_item.remove_attrs_and_docs();
+ cloned_item.reindent_to(new_indent_level);
+ cloned_item
});
- let res = impl_.clone_for_update();
-
- let assoc_item_list = res.get_or_create_assoc_item_list();
+ let assoc_item_list = impl_.get_or_create_assoc_item_list();
let mut first_item = None;
for item in items {
first_item.get_or_insert_with(|| item.clone());
match &item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let body = make::block_expr(None, Some(make::ext::expr_todo()))
- .indent(edit::IndentLevel(1));
+ let body = AstNodeEdit::indent(
+ &make::block_expr(None, Some(make::ext::expr_todo())),
+ new_indent_level,
+ );
ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
}
ast::AssocItem::TypeAlias(type_alias) => {
@@ -168,7 +193,7 @@ pub fn add_trait_assoc_items_to_impl(
assoc_item_list.add_item(item)
}
- (res, first_item.unwrap())
+ first_item.unwrap()
}
#[derive(Clone, Copy, Debug)]
@@ -338,7 +363,12 @@ fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
/// `find_struct_impl` looks for impl of a struct, but this also has additional feature
/// where it takes a list of function names and check if they exist inside impl_, if
-/// even one match is found, it returns None
+/// even one match is found, it returns None.
+///
+/// That means this function can have 3 potential return values:
+/// - `None`: an impl exists, but one of the function names within the impl matches one of the provided names.
+/// - `Some(None)`: no impl exists.
+/// - `Some(Some(_))`: an impl exists, with no matching function names.
pub(crate) fn find_struct_impl(
ctx: &AssistContext<'_>,
adt: &ast::Adt,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index c521a10fc..f74ebfae0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -234,7 +234,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<Str
fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
let name = if let Some(adt) = ty.as_adt() {
- let name = adt.name(db).to_string();
+ let name = adt.name(db).display(db).to_string();
if WRAPPER_TYPES.contains(&name.as_str()) {
let inner_ty = ty.type_arguments().next()?;
@@ -258,7 +258,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
}
fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
- let name = trait_.name(db).to_string();
+ let name = trait_.name(db).display(db).to_string();
if USELESS_TRAITS.contains(&name.as_str()) {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index c3136f6df..480cb77b4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -23,8 +23,8 @@ pub(crate) mod env_vars;
use std::iter;
-use hir::{known, ScopeDef, Variant};
-use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
+use hir::{known, HasAttrs, ScopeDef, Variant};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind};
use syntax::ast;
use crate::{
@@ -62,8 +62,8 @@ impl From<Completions> for Vec<CompletionItem> {
impl Builder {
/// Convenience method, which allows to add a freshly created completion into accumulator
/// without binding it to the variable.
- pub(crate) fn add_to(self, acc: &mut Completions) {
- acc.add(self.build())
+ pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) {
+ acc.add(self.build(db))
}
}
@@ -78,17 +78,9 @@ impl Completions {
}
}
- pub(crate) fn add_all<I>(&mut self, items: I)
- where
- I: IntoIterator,
- I::Item: Into<CompletionItem>,
- {
- items.into_iter().for_each(|item| self.add(item.into()))
- }
-
pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) {
let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword);
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) {
@@ -142,7 +134,7 @@ impl Completions {
item.insert_text(if snippet.contains('$') { kw } else { snippet });
}
};
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_keyword_snippet(
@@ -157,7 +149,7 @@ impl Completions {
Some(cap) => item.insert_snippet(cap, snippet),
None => item.insert_text(if snippet.contains('$') { kw } else { snippet }),
};
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_crate_roots(
@@ -165,9 +157,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
path_ctx: &PathCompletionCtx,
) {
- ctx.process_all_names(&mut |name, res| match res {
- ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
- self.add_module(ctx, path_ctx, m, name);
+ ctx.process_all_names(&mut |name, res, doc_aliases| match res {
+ ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => {
+ self.add_module(ctx, path_ctx, m, name, doc_aliases);
}
_ => (),
});
@@ -179,7 +171,11 @@ impl Completions {
path_ctx: &PathCompletionCtx,
local_name: hir::Name,
resolution: hir::ScopeDef,
+ doc_aliases: Vec<syntax::SmolStr>,
) {
+ if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+ return;
+ }
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@@ -187,12 +183,14 @@ impl Completions {
};
self.add(
render_path_resolution(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
path_ctx,
local_name,
resolution,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -203,6 +201,9 @@ impl Completions {
local_name: hir::Name,
resolution: hir::ScopeDef,
) {
+ if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+ return;
+ }
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@@ -215,7 +216,7 @@ impl Completions {
local_name,
resolution,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -225,6 +226,9 @@ impl Completions {
path_ctx: &PathCompletionCtx,
e: hir::Enum,
) {
+ if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
+ return;
+ }
e.variants(ctx.db)
.into_iter()
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -236,12 +240,17 @@ impl Completions {
path_ctx: &PathCompletionCtx,
module: hir::Module,
local_name: hir::Name,
+ doc_aliases: Vec<syntax::SmolStr>,
) {
+ if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
+ return;
+ }
self.add_path_resolution(
ctx,
path_ctx,
local_name,
hir::ScopeDef::ModuleDef(module.into()),
+ doc_aliases,
);
}
@@ -252,6 +261,9 @@ impl Completions {
mac: hir::Macro,
local_name: hir::Name,
) {
+ if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&mac) {
Visible::Yes => false,
Visible::Editable => true,
@@ -264,7 +276,7 @@ impl Completions {
local_name,
mac,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -275,19 +287,25 @@ impl Completions {
func: hir::Function,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_fn(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
path_ctx,
local_name,
func,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -299,20 +317,26 @@ impl Completions {
receiver: Option<hir::Name>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_method(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
dot_access,
receiver,
local_name,
func,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -323,26 +347,34 @@ impl Completions {
func: hir::Function,
import: LocatedImport,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_method(
RenderContext::new(ctx)
.private_editable(is_private_editable)
+ .doc_aliases(doc_aliases)
.import_to_add(Some(import)),
dot_access,
None,
None,
func,
)
- .build(),
+ .build(ctx.db),
);
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+ if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false,
Visible::Editable => true,
@@ -359,6 +391,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
+ if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&type_alias) {
Visible::Yes => false,
Visible::Editable => true,
@@ -375,6 +410,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
+ if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
}
@@ -385,10 +423,13 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
if let Some(builder) =
render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -399,6 +440,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
cov_mark::hit!(enum_variant_pattern_path);
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@@ -408,7 +452,7 @@ impl Completions {
if let Some(builder) =
render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None)
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -420,13 +464,17 @@ impl Completions {
field: hir::Field,
ty: &hir::Type,
) {
+ if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&field) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&field);
let item = render_field(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
dot_access,
receiver,
field,
@@ -443,10 +491,13 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+ return;
+ }
if let Some(builder) =
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -457,6 +508,9 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
+ return;
+ }
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
self.add_opt(item);
}
@@ -468,17 +522,20 @@ impl Completions {
field: usize,
ty: &hir::Type,
) {
+ // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
+ // stability here.
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str())
- .add_to(self)
+ .add_to(self, ctx.db)
}
pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
- CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self)
+ CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str())
+ .add_to(self, ctx.db)
}
pub(crate) fn add_variant_pat(
@@ -489,6 +546,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
pattern_ctx,
@@ -506,6 +566,9 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
let path = Some(&path);
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
@@ -524,6 +587,9 @@ impl Completions {
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index bb950c76f..466f0b1fb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -93,7 +93,7 @@ pub(crate) fn complete_attribute_path(
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -104,12 +104,12 @@ pub(crate) fn complete_attribute_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
// only show modules in a fresh UseTree
Qualified::No => {
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, doc_aliases)
}
_ => (),
});
@@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path(
}
if is_inner || !attr_completion.prefer_inner {
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 7ef4ff30b..19bfd294b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
let add_completion = |item: &str| {
let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item);
completion.insert_text(format!(r#""{item}""#));
- acc.add(completion.build());
+ acc.add(completion.build(ctx.db));
};
let previous = iter::successors(ctx.original_token.prev_token(), |t| {
@@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}),
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}),
};
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 793c22630..9447bc7db 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -34,7 +34,7 @@ pub(crate) fn complete_derive_path(
acc.add_macro(ctx, path_ctx, mac, name)
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -43,7 +43,7 @@ pub(crate) fn complete_derive_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
// only show modules in a fresh UseTree
Qualified::No => {
- ctx.process_all_names(&mut |name, def| {
+ ctx.process_all_names(&mut |name, def, doc_aliases| {
let mac = match def {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
@@ -51,7 +51,7 @@ pub(crate) fn complete_derive_path(
mac
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- return acc.add_module(ctx, path_ctx, m, name);
+ return acc.add_module(ctx, path_ctx, m, name, doc_aliases);
}
_ => return,
};
@@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path(
item.documentation(docs);
}
item.lookup_by(lookup);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
None => acc.add_macro(ctx, path_ctx, mac, name),
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index 818c3cfd5..6bc6f34ed 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -56,6 +56,6 @@ pub(super) fn complete_lint(
};
let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
item.documentation(hir::Documentation::new(description.to_owned()));
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
index a29417133..14f464b77 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
@@ -37,7 +37,7 @@ pub(super) fn complete_repr(
if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
item.insert_snippet(cap, snippet);
}
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 77246379e..c5bbb7f8d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_dot(
let mut item =
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
item.detail("expr.await");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
if let DotAccessKind::Method { .. } = dot_access.kind {
@@ -105,13 +105,20 @@ fn complete_fields(
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
) {
+ let mut seen_names = FxHashSet::default();
for receiver in receiver.autoderef(ctx.db) {
for (field, ty) in receiver.fields(ctx.db) {
- named_field(acc, field, ty);
+ if seen_names.insert(field.name(ctx.db)) {
+ named_field(acc, field, ty);
+ }
}
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
- // Tuple fields are always public (tuple struct fields are handled above).
- tuple_index(acc, i, ty);
+ // Tuples are always the last type in a deref chain, so just check if the name is
+ // already seen without inserting into the hashset.
+ if !seen_names.contains(&hir::Name::new_tuple_field(i)) {
+ // Tuple fields are always public (tuple struct fields are handled above).
+ tuple_index(acc, i, ty);
+ }
}
}
}
@@ -173,6 +180,43 @@ fn foo(s: S) { s.$0 }
}
#[test]
+ fn no_unstable_method_on_stable() {
+ check(
+ r#"
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+ #[unstable]
+ pub fn bar(&self) {}
+}
+"#,
+ expect![""],
+ );
+ }
+
+ #[test]
+ fn unstable_method_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+ #[unstable]
+ pub fn bar(&self) {}
+}
+"#,
+ expect![[r#"
+ me bar() fn(&self)
+ "#]],
+ );
+ }
+
+ #[test]
fn test_struct_field_completion_self() {
check(
r#"
@@ -635,6 +679,74 @@ impl T {
}
#[test]
+ fn test_field_no_same_name() {
+ check(
+ r#"
+//- minicore: deref
+struct A { field: u8 }
+struct B { field: u16, another: u32 }
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd another u32
+ fd field u8
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_field_no_same_index() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16, u32);
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ fd 1 u32
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_struct_deref_to_tuple_no_same_index() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+impl core::ops::Deref for A {
+ type Target = (u16, u32);
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ fd 1 u32
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
fn test_completion_works_in_consts() {
check(
r#"
@@ -942,4 +1054,45 @@ fn test(thing: impl Encrypt) {
"#]],
)
}
+
+ #[test]
+ fn only_consider_same_type_once() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16);
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+impl core::ops::Deref for B {
+ type Target = A;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn no_inference_var_in_completion() {
+ check(
+ r#"
+struct S<T>(T);
+fn test(s: S<Unknown>) {
+ s.$0
+}
+"#,
+ expect![[r#"
+ fd 0 {unknown}
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
index 1002be211..419b86456 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -37,10 +37,10 @@ pub(crate) fn complete_cargo_env_vars(
guard_env_macro(expanded, &ctx.sema)?;
let range = expanded.text_range_between_quotes()?;
- CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
+ CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
- item.detail(*detail);
- item.add_to(acc);
+ item.detail(detail);
+ item.add_to(acc, ctx.db);
});
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index cfe4787f7..9daa6984c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path(
let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(
+ ctx,
+ path_ctx,
+ name,
+ def,
+ ctx.doc_aliases_in_scope(def),
+ );
}
}
}
@@ -212,7 +218,7 @@ pub(crate) fn complete_expr_path(
}
}
}
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
let assocs = t.items_with_supertraits(ctx.db);
match &*assocs {
@@ -220,12 +226,14 @@ pub(crate) fn complete_expr_path(
// there is no associated item path that can be constructed with them
[] => (),
// FIXME: Render the assoc item with the trait qualified
- &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
+ &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
// FIXME: Append `::` to the thing here, since a trait on its own won't work
- [..] => acc.add_path_resolution(ctx, path_ctx, name, def),
+ [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
}
}
- _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
+ _ if scope_def_applicable(def) => {
+ acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
+ }
_ => (),
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
index 4e89ef696..c717a9cb5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
@@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
pub(crate) fn complete_extern_abi(
acc: &mut Completions,
- _ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_>,
expanded: &ast::String,
) -> Option<()> {
if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) {
@@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi(
let abi_str = expanded;
let source_range = abi_str.text_range_between_quotes()?;
for &abi in SUPPORTED_CALLING_CONVENTIONS {
- CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
+ CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db);
}
Some(())
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 0979f6a6d..39c1b7f7b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -257,30 +257,24 @@ fn import_on_the_fly(
};
let user_input_lowercased = potential_import_name.to_lowercase();
- acc.add_all(
- import_assets
- .search_for_imports(
- &ctx.sema,
- ctx.config.insert_use.prefix_kind,
- ctx.config.prefer_no_std,
- )
- .into_iter()
- .filter(ns_filter)
- .filter(|import| {
- !ctx.is_item_hidden(&import.item_to_import)
- && !ctx.is_item_hidden(&import.original_item)
- })
- .sorted_by_key(|located_import| {
- compute_fuzzy_completion_order_key(
- &located_import.import_path,
- &user_input_lowercased,
- )
- })
- .filter_map(|import| {
- render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
- })
- .map(|builder| builder.build()),
- );
+ import_assets
+ .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+ .into_iter()
+ .filter(ns_filter)
+ .filter(|import| {
+ let original_item = &import.original_item;
+ !ctx.is_item_hidden(&import.item_to_import)
+ && !ctx.is_item_hidden(original_item)
+ && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+ })
+ .sorted_by_key(|located_import| {
+ compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+ })
+ .filter_map(|import| {
+ render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
+ })
+ .map(|builder| builder.build(ctx.db))
+ .for_each(|item| acc.add(item));
Some(())
}
@@ -305,30 +299,24 @@ fn import_on_the_fly_pat_(
};
let user_input_lowercased = potential_import_name.to_lowercase();
- acc.add_all(
- import_assets
- .search_for_imports(
- &ctx.sema,
- ctx.config.insert_use.prefix_kind,
- ctx.config.prefer_no_std,
- )
- .into_iter()
- .filter(ns_filter)
- .filter(|import| {
- !ctx.is_item_hidden(&import.item_to_import)
- && !ctx.is_item_hidden(&import.original_item)
- })
- .sorted_by_key(|located_import| {
- compute_fuzzy_completion_order_key(
- &located_import.import_path,
- &user_input_lowercased,
- )
- })
- .filter_map(|import| {
- render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
- })
- .map(|builder| builder.build()),
- );
+ import_assets
+ .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+ .into_iter()
+ .filter(ns_filter)
+ .filter(|import| {
+ let original_item = &import.original_item;
+ !ctx.is_item_hidden(&import.item_to_import)
+ && !ctx.is_item_hidden(original_item)
+ && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+ })
+ .sorted_by_key(|located_import| {
+ compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+ })
+ .filter_map(|import| {
+ render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
+ })
+ .map(|builder| builder.build(ctx.db))
+ .for_each(|item| acc.add(item));
Some(())
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
index d8b8a190e..8b38d4f01 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param(
};
// Completion lookup is omitted intentionally here.
// See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
};
match kind {
@@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param(
ParamKind::Closure(closure) => {
let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
- add_new_item_to_acc(&format!("{name}: {ty}"));
+ add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db)));
});
}
}
@@ -100,7 +100,9 @@ fn fill_fn_params(
if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) {
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
- file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string());
+ file_params
+ .entry(format!("{}: {ty}", name.display(ctx.db)))
+ .or_insert(name.display(ctx.db).to_string());
});
}
remove_duplicated(&mut file_params, param_list.params());
@@ -127,7 +129,7 @@ fn params_from_stmt_list_scope(
let module = scope.module().into();
scope.process_all_names(&mut |name, def| {
if let hir::ScopeDef::Local(local) = def {
- if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) {
+ if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) {
cb(name, ty);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 5c46c5806..8e904fd60 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -32,7 +32,7 @@ pub(crate) fn format_string(
let source_range = TextRange::new(brace_offset, cursor);
ctx.locals.iter().for_each(|(name, _)| {
CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
- .add_to(acc);
+ .add_to(acc, ctx.db);
})
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
index 60d05ae46..5ea6a49b1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
@@ -45,7 +45,7 @@ pub(crate) fn complete_item_list(
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -55,12 +55,12 @@ pub(crate) fn complete_item_list(
}
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
Qualified::No if ctx.qualifier_ctx.none() => {
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, doc_aliases)
}
_ => (),
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 889d90095..269e40e6e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -150,21 +150,24 @@ fn complete_trait_impl(
impl_def: &ast::Impl,
) {
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
- get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
- use self::ImplCompletionKind::*;
- match (item, kind) {
- (hir::AssocItem::Function(func), All | Fn) => {
- add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+ get_missing_assoc_items(&ctx.sema, impl_def)
+ .into_iter()
+ .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
+ .for_each(|item| {
+ use self::ImplCompletionKind::*;
+ match (item, kind) {
+ (hir::AssocItem::Function(func), All | Fn) => {
+ add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+ }
+ (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
+ add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
+ }
+ (hir::AssocItem::Const(const_), All | Const) => {
+ add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
+ }
+ _ => {}
}
- (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
- add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
- }
- (hir::AssocItem::Const(const_), All | Const) => {
- add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
- }
- _ => {}
- }
- });
+ });
}
}
@@ -179,7 +182,7 @@ fn add_function_impl(
let label = format!(
"fn {}({})",
- fn_name,
+ fn_name.display(ctx.db),
if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
);
@@ -190,7 +193,7 @@ fn add_function_impl(
};
let mut item = CompletionItem::new(completion_kind, replacement_range, label);
- item.lookup_by(format!("fn {fn_name}"))
+ item.lookup_by(format!("fn {}", fn_name.display(ctx.db)))
.set_documentation(func.docs(ctx.db))
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
@@ -213,7 +216,7 @@ fn add_function_impl(
item.text_edit(TextEdit::replace(replacement_range, header));
}
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -224,9 +227,8 @@ fn get_transformed_assoc_item(
assoc_item: ast::AssocItem,
impl_def: hir::Impl,
) -> Option<ast::AssocItem> {
- let assoc_item = assoc_item.clone_for_update();
let trait_ = impl_def.trait_(ctx.db)?;
- let source_scope = &ctx.sema.scope_for_def(trait_);
+ let source_scope = &ctx.sema.scope(assoc_item.syntax())?;
let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
let transform = PathTransform::trait_impl(
target_scope,
@@ -235,6 +237,9 @@ fn get_transformed_assoc_item(
ctx.sema.source(impl_def)?.value,
);
+ let assoc_item = assoc_item.clone_for_update();
+ // FIXME: Paths in nested macros are not handled well. See
+ // `macro_generated_assoc_item2` test.
transform.apply(assoc_item.syntax());
assoc_item.remove_attrs_and_docs();
Some(assoc_item)
@@ -297,7 +302,7 @@ fn add_type_alias_impl(
item.text_edit(TextEdit::replace(replacement_range, decl));
}
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -337,7 +342,7 @@ fn add_const_impl(
),
None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -831,6 +836,33 @@ impl Test for () {
}
#[test]
+ fn fn_with_lifetimes() {
+ check_edit(
+ "fn foo",
+ r#"
+trait Test<'a, 'b, T> {
+ fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+ t$0
+}
+"#,
+ r#"
+trait Test<'a, 'b, T> {
+ fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+ fn foo(&self, a: &'x A, b: &'y A) -> &'x A {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ #[test]
fn complete_without_name() {
let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
check_edit(
@@ -1191,6 +1223,81 @@ impl Foo for Test {
}
#[test]
+ fn macro_generated_assoc_item() {
+ check_edit(
+ "fn method",
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ $0
+}
+"#,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+ #[test]
+ fn macro_generated_assoc_item2() {
+ check_edit(
+ "fn method",
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ $0
+}
+"#,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ #[test]
fn includes_gat_generics() {
check_edit(
"type Ty",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
index 3b79def63..2c6cbf614 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
@@ -329,6 +329,7 @@ fn foo() {
fn complete_label_in_for_iterable() {
check(
r#"
+//- minicore: iterator
fn foo() {
'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 950731eb4..d3e75c6da 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -52,7 +52,7 @@ pub(crate) fn complete_mod(
let existing_mod_declarations = current_module
.children(ctx.db)
- .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+ .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string()))
.filter(|module| module != ctx.original_token.text())
.collect::<FxHashSet<_>>();
@@ -99,7 +99,7 @@ pub(crate) fn complete_mod(
label.push(';');
}
let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
});
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index 58d5bf114..40b2c831a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_pattern(
// FIXME: ideally, we should look at the type we are matching against and
// suggest variants + auto-imports
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, _| {
let add_simple_path = match res {
hir::ScopeDef::ModuleDef(def) => match def {
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
@@ -127,7 +127,7 @@ pub(crate) fn complete_pattern_path(
};
if add_resolution {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
}
}
}
@@ -164,7 +164,7 @@ pub(crate) fn complete_pattern_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
Qualified::No => {
// this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
// FIXME: we should check what kind of pattern we are in and filter accordingly
let add_completion = match res {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
@@ -175,7 +175,7 @@ pub(crate) fn complete_pattern_path(
_ => false,
};
if add_completion {
- acc.add_path_resolution(ctx, path_ctx, name, res);
+ acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
}
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index c55bd9aaa..2ffe12337 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_postfix(
&format!("drop($0{receiver_text})"),
);
item.set_documentation(drop_fn.docs(ctx.db));
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -78,14 +78,14 @@ pub(crate) fn complete_postfix(
"if let Ok {}",
&format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet(
"while",
"while let Ok {}",
&format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
TryEnum::Option => {
postfix_snippet(
@@ -93,22 +93,22 @@ pub(crate) fn complete_postfix(
"if let Some {}",
&format!("if let Some($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet(
"while",
"while let Some {}",
&format!("while let Some($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
} else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}"))
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}"))
- .add_to(acc);
- postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc);
+ .add_to(acc, ctx.db);
+ postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
} else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
postfix_snippet(
@@ -116,12 +116,12 @@ pub(crate) fn complete_postfix(
"for ele in expr {}",
&format!("for ele in {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
- postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc);
- postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc);
+ postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);
+ postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);
let mut unsafe_should_be_wrapped = true;
if dot_receiver.syntax().kind() == BLOCK_EXPR {
@@ -137,7 +137,7 @@ pub(crate) fn complete_postfix(
} else {
format!("unsafe {receiver_text}")
};
- postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc);
+ postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
// The rest of the postfix completions create an expression that moves an argument,
// so it's better to consider references now to avoid breaking the compilation
@@ -162,7 +162,7 @@ pub(crate) fn complete_postfix(
"match expr {}",
&format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
TryEnum::Option => {
postfix_snippet(
@@ -172,7 +172,7 @@ pub(crate) fn complete_postfix(
"match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}"
),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
},
None => {
@@ -181,20 +181,23 @@ pub(crate) fn complete_postfix(
"match expr {}",
&format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
- postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc);
- postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme
- postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc);
- postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc);
+ postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
+ .add_to(acc, ctx.db);
+ postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
+ postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
+ postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
+ .add_to(acc, ctx.db);
if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
- postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc);
+ postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
+ .add_to(acc, ctx.db);
postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
@@ -315,7 +318,7 @@ fn add_custom_postfix_completions(
for import in imports.into_iter() {
builder.add_import(import);
}
- builder.add_to(acc);
+ builder.add_to(acc, ctx.db);
},
);
None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index dfcc78e92..cb242e4aa 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions(
format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
};
- postfix_snippet(label, macro_name, &snippet).add_to(acc);
+ postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 0521e735d..945c3945b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields(
let mut item =
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
item.insert_text(".");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
return;
}
missing_fields
@@ -98,7 +98,7 @@ pub(crate) fn add_default_update(
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
..Default::default()
});
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index da1f0542d..e9831a5b2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet(
}
if in_block_expr {
- snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
- snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
+ snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db);
+ snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db);
let item = snippet(
ctx,
cap,
@@ -45,7 +45,7 @@ macro_rules! $1 {
};
}",
);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
@@ -88,7 +88,7 @@ mod tests {
}",
);
item.lookup_by("tmod");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
let mut item = snippet(
ctx,
@@ -101,7 +101,7 @@ fn ${1:feature}() {
}",
);
item.lookup_by("tfn");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
let item = snippet(
ctx,
@@ -114,7 +114,7 @@ macro_rules! $1 {
};
}",
);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
@@ -146,7 +146,7 @@ fn add_custom_completions(
builder.add_import(import);
}
builder.set_detail(snip.description.clone());
- builder.add_to(acc);
+ builder.add_to(acc, ctx.db);
},
);
None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index 69c05a76d..e47054756 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -85,7 +85,7 @@ pub(crate) fn complete_type_path(
let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
}
}
}
@@ -141,7 +141,7 @@ pub(crate) fn complete_type_path(
match location {
TypeLocation::TypeBound => {
acc.add_nameref_keywords_with_colon(ctx);
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
let add_resolution = match res {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
mac.is_fn_like(ctx.db)
@@ -152,7 +152,7 @@ pub(crate) fn complete_type_path(
_ => false,
};
if add_resolution {
- acc.add_path_resolution(ctx, path_ctx, name, res);
+ acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
}
});
return;
@@ -215,9 +215,9 @@ pub(crate) fn complete_type_path(
};
acc.add_nameref_keywords_with_colon(ctx);
- ctx.process_all_names(&mut |name, def| {
+ ctx.process_all_names(&mut |name, def, doc_aliases| {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
}
});
}
@@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type(
}
}?
.adjusted();
- let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
+ let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
acc.add(render_type_inference(ty_string, ctx));
None
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
index 2555c34aa..7a60030e9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
@@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
)
};
for (name, def) in module_scope {
+ if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
+ continue;
+ }
let is_name_already_imported = name
.as_text()
.map_or(false, |text| already_imported_names.contains(text.as_str()));
@@ -72,7 +75,7 @@ pub(crate) fn complete_use_path(
is_name_already_imported,
..Default::default()
});
- acc.add(builder.build());
+ acc.add(builder.build(ctx.db));
}
}
}
@@ -91,10 +94,10 @@ pub(crate) fn complete_use_path(
// only show modules and non-std enum in a fresh UseTree
Qualified::No => {
cov_mark::hit!(unqualified_path_selected_only);
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
match res {
ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
- acc.add_module(ctx, path_ctx, module, name);
+ acc.add_module(ctx, path_ctx, module, name, doc_aliases);
}
ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
// exclude prelude enum
@@ -105,9 +108,9 @@ pub(crate) fn complete_use_path(
let item = CompletionItem::new(
CompletionItemKind::SymbolKind(SymbolKind::Enum),
ctx.source_range(),
- format!("{}::", e.name(ctx.db)),
+ format!("{}::", e.name(ctx.db).display(ctx.db)),
);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}
}
_ => {}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
index 5e6cf4bf9..e0a959ad0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path(
if let Some(next) = next_towards_current {
if let Some(name) = next.name(ctx.db) {
cov_mark::hit!(visibility_qualified);
- acc.add_module(ctx, path_ctx, next, name);
+ acc.add_module(ctx, path_ctx, next, name, vec![]);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 8cbf89e9c..7b145f3c1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -17,7 +17,7 @@ use ide_db::{
};
use syntax::{
ast::{self, AttrKind, NameOrNameRef},
- AstNode,
+ AstNode, SmolStr,
SyntaxKind::{self, *},
SyntaxToken, TextRange, TextSize, T,
};
@@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) krate: hir::Crate,
/// The module of the `scope`.
pub(super) module: hir::Module,
+ /// Whether nightly toolchain is used. Cached since this is looked up a lot.
+ is_nightly: bool,
/// The expected name of what we are completing.
/// This is usually the parameter name of the function argument we are completing.
@@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) depth_from_crate_root: usize,
}
-impl<'a> CompletionContext<'a> {
+impl CompletionContext<'_> {
/// The range of the identifier that is being completed.
pub(crate) fn source_range(&self) -> TextRange {
let kind = self.original_token.kind();
@@ -441,6 +443,14 @@ impl<'a> CompletionContext<'a> {
self.is_visible_impl(&vis, &attrs, item.krate(self.db))
}
+ pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
+ where
+ I: hir::HasAttrs + Copy,
+ {
+ let attrs = item.attrs(self.db);
+ attrs.doc_aliases().collect()
+ }
+
/// Check if an item is `#[doc(hidden)]`.
pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
let attrs = item.attrs(self.db);
@@ -451,6 +461,12 @@ impl<'a> CompletionContext<'a> {
}
}
+ /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
+ pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
+ let Some(attrs) = attrs else { return true; };
+ !attrs.is_unstable() || self.is_nightly
+ }
+
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() {
@@ -491,21 +507,22 @@ impl<'a> CompletionContext<'a> {
);
}
- /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
- pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
+ /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
+ /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`].
+ pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
let _p = profile::span("CompletionContext::process_all_names");
self.scope.process_all_names(&mut |name, def| {
if self.is_scope_def_hidden(def) {
return;
}
-
- f(name, def);
+ let doc_aliases = self.doc_aliases_in_scope(def);
+ f(name, def, doc_aliases);
});
}
pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let _p = profile::span("CompletionContext::process_all_names_raw");
- self.scope.process_all_names(&mut |name, def| f(name, def));
+ self.scope.process_all_names(f);
}
fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
@@ -545,6 +562,14 @@ impl<'a> CompletionContext<'a> {
// `doc(hidden)` items are only completed within the defining crate.
self.krate != defining_crate && attrs.has_doc_hidden()
}
+
+ pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
+ if let Some(attrs) = scope_def.attrs(self.db) {
+ attrs.doc_aliases().collect()
+ } else {
+ vec![]
+ }
+ }
}
// CompletionContext construction
@@ -615,6 +640,11 @@ impl<'a> CompletionContext<'a> {
let krate = scope.krate();
let module = scope.module();
+ let toolchain = db.crate_graph()[krate.into()].channel;
+ // `toolchain == None` means we're in some detached files. Since we have no information on
+ // the toolchain being used, let's just allow unstable items to be listed.
+ let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
+
let mut locals = FxHashMap::default();
scope.process_all_names(&mut |name, scope| {
if let ScopeDef::Local(local) = scope {
@@ -634,6 +664,7 @@ impl<'a> CompletionContext<'a> {
token,
krate,
module,
+ is_nightly,
expected_name,
expected_type,
qualifier_ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index a94c40458..cc5221cfc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -609,14 +609,14 @@ fn classify_name_ref(
_ => false,
};
- let reciever_is_part_of_indivisible_expression = match &receiver {
+ let receiver_is_part_of_indivisible_expression = match &receiver {
Some(ast::Expr::IfExpr(_)) => {
let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind());
next_token_kind == Some(SyntaxKind::ELSE_KW)
},
_ => false
};
- if reciever_is_part_of_indivisible_expression {
+ if receiver_is_part_of_indivisible_expression {
return None;
}
@@ -1190,7 +1190,7 @@ fn pattern_context_for(
})
}).and_then(|variants| {
Some(variants.iter().filter_map(|variant| {
- let variant_name = variant.name(sema.db).to_string();
+ let variant_name = variant.name(sema.db).display(sema.db).to_string();
let variant_already_present = match_arm_list.arms().any(|arm| {
arm.pat().and_then(|pat| {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index bb9fa7cca..e850f7bfd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -3,7 +3,8 @@
use std::fmt;
use hir::{Documentation, Mutability};
-use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind};
+use itertools::Itertools;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::{SmolStr, TextRange, TextSize};
@@ -45,7 +46,7 @@ pub struct CompletionItem {
///
/// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
/// contains `bar` sub sequence), and `quux` will rejected.
- pub lookup: Option<SmolStr>,
+ pub lookup: SmolStr,
/// Additional info to show in the UI pop up.
pub detail: Option<String>,
@@ -75,7 +76,8 @@ pub struct CompletionItem {
pub ref_match: Option<(Mutability, TextSize)>,
/// The import data to add to completion's edits.
- pub import_to_add: SmallVec<[LocatedImport; 1]>,
+ /// (ImportPath, LastSegment)
+ pub import_to_add: SmallVec<[(String, String); 1]>,
}
// We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -353,12 +355,13 @@ impl CompletionItem {
relevance: CompletionRelevance::default(),
ref_match: None,
imports_to_add: Default::default(),
+ doc_aliases: vec![],
}
}
/// What string is used for filtering.
pub fn lookup(&self) -> &str {
- self.lookup.as_deref().unwrap_or(&self.label)
+ self.lookup.as_str()
}
pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
@@ -385,6 +388,7 @@ pub(crate) struct Builder {
source_range: TextRange,
imports_to_add: SmallVec<[LocatedImport; 1]>,
trait_name: Option<SmolStr>,
+ doc_aliases: Vec<SmolStr>,
label: SmolStr,
insert_text: Option<String>,
is_snippet: bool,
@@ -406,21 +410,31 @@ impl Builder {
local_name: hir::Name,
resolution: hir::ScopeDef,
) -> Self {
- render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
+ let doc_aliases = ctx.doc_aliases_in_scope(resolution);
+ render_path_resolution(
+ RenderContext::new(ctx).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ resolution,
+ )
}
- pub(crate) fn build(self) -> CompletionItem {
+ pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem {
let _p = profile::span("item::Builder::build");
let mut label = self.label;
- let mut lookup = self.lookup;
+ let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
+ if !self.doc_aliases.is_empty() {
+ let doc_aliases = self.doc_aliases.into_iter().join(", ");
+ label = SmolStr::from(format!("{label} (alias {doc_aliases})"));
+ lookup = SmolStr::from(format!("{lookup} {doc_aliases}"));
+ }
if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one
if let Some(original_path) = import_edit.original_path.as_ref() {
- lookup = lookup.or_else(|| Some(label.clone()));
- label = SmolStr::from(format!("{label} (use {original_path})"));
+ label = SmolStr::from(format!("{label} (use {})", original_path.display(db)));
}
} else if let Some(trait_name) = self.trait_name {
label = SmolStr::from(format!("{label} (as {trait_name})"));
@@ -431,6 +445,17 @@ impl Builder {
None => TextEdit::replace(self.source_range, insert_text),
};
+ let import_to_add = self
+ .imports_to_add
+ .into_iter()
+ .filter_map(|import| {
+ Some((
+ import.import_path.display(db).to_string(),
+ import.import_path.segments().last()?.display(db).to_string(),
+ ))
+ })
+ .collect();
+
CompletionItem {
source_range: self.source_range,
label,
@@ -444,7 +469,7 @@ impl Builder {
trigger_call_info: self.trigger_call_info,
relevance: self.relevance,
ref_match: self.ref_match,
- import_to_add: self.imports_to_add,
+ import_to_add,
}
}
pub(crate) fn lookup_by(&mut self, lookup: impl Into<SmolStr>) -> &mut Builder {
@@ -459,6 +484,10 @@ impl Builder {
self.trait_name = Some(trait_name);
self
}
+ pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec<SmolStr>) -> &mut Builder {
+ self.doc_aliases = doc_aliases;
+ self
+ }
pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
self.insert_text = Some(insert_text.into());
self
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 6fe781114..106d4e1e5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -97,7 +97,7 @@ pub use crate::{
/// Main entry point for completion. We run completion as a two-phase process.
///
-/// First, we look at the position and collect a so-called `CompletionContext.
+/// First, we look at the position and collect a so-called `CompletionContext`.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
@@ -133,7 +133,7 @@ pub use crate::{
///
/// Another case where this would be instrumental is macro expansion. We want to
/// insert a fake ident and re-expand code. There's `expand_speculative` as a
-/// work-around for this.
+/// workaround for this.
///
/// A different use-case is completion of injection (examples and links in doc
/// comments). When computing completion for a path in a doc-comment, you want
@@ -243,7 +243,7 @@ pub fn resolve_completion_edits(
config.prefer_no_std,
)
})
- .find(|mod_path| mod_path.to_string() == full_import_path);
+ .find(|mod_path| mod_path.display(db).to_string() == full_import_path);
if let Some(import_path) = import {
insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use);
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index c1f51aabb..1953eb479 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -32,11 +32,17 @@ pub(crate) struct RenderContext<'a> {
completion: &'a CompletionContext<'a>,
is_private_editable: bool,
import_to_add: Option<LocatedImport>,
+ doc_aliases: Vec<SmolStr>,
}
impl<'a> RenderContext<'a> {
pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
- RenderContext { completion, is_private_editable: false, import_to_add: None }
+ RenderContext {
+ completion,
+ is_private_editable: false,
+ import_to_add: None,
+ doc_aliases: vec![],
+ }
}
pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
@@ -49,6 +55,11 @@ impl<'a> RenderContext<'a> {
self
}
+ pub(crate) fn doc_aliases(mut self, doc_aliases: Vec<SmolStr>) -> Self {
+ self.doc_aliases = doc_aliases;
+ self
+ }
+
fn snippet_cap(&self) -> Option<SnippetCap> {
self.completion.config.snippet_cap
}
@@ -115,24 +126,25 @@ pub(crate) fn render_field(
field: hir::Field,
ty: &hir::Type,
) -> CompletionItem {
+ let db = ctx.db();
let is_deprecated = ctx.is_deprecated(field);
- let name = field.name(ctx.db());
+ let name = field.name(db);
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(receiver.as_ref(), &name),
+ field_with_receiver(db, receiver.as_ref(), &name),
);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(ctx.completion, ty),
exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
..CompletionRelevance::default()
});
- item.detail(ty.display(ctx.db()).to_string())
- .set_documentation(field.docs(ctx.db()))
+ item.detail(ty.display(db).to_string())
+ .set_documentation(field.docs(db))
.set_deprecated(is_deprecated)
.lookup_by(name);
- item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name));
+ item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
if let Some(receiver) = &dot_access.receiver {
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
@@ -140,11 +152,19 @@ pub(crate) fn render_field(
}
}
}
- item.build()
+ item.doc_aliases(ctx.doc_aliases);
+ item.build(db)
}
-fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr {
- receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into())
+fn field_with_receiver(
+ db: &RootDatabase,
+ receiver: Option<&hir::Name>,
+ field_name: &str,
+) -> SmolStr {
+ receiver.map_or_else(
+ || field_name.into(),
+ |receiver| format!("{}.{field_name}", receiver.display(db)).into(),
+ )
}
pub(crate) fn render_tuple_field(
@@ -156,10 +176,10 @@ pub(crate) fn render_tuple_field(
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(receiver.as_ref(), &field.to_string()),
+ field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()),
);
item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
- item.build()
+ item.build(ctx.db())
}
pub(crate) fn render_type_inference(
@@ -169,7 +189,7 @@ pub(crate) fn render_type_inference(
let mut builder =
CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
- builder.build()
+ builder.build(ctx.db)
}
pub(crate) fn render_path_resolution(
@@ -197,7 +217,9 @@ pub(crate) fn render_resolution_with_import(
) -> Option<Builder> {
let resolution = ScopeDef::from(import_edit.original_item);
let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
-
+ //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead
+ let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);
+ let ctx = ctx.doc_aliases(doc_aliases);
Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))
}
@@ -305,7 +327,7 @@ fn render_resolution_path(
item.lookup_by(name.clone())
.label(SmolStr::from_iter([&name, "<…>"]))
.trigger_call_info()
- .insert_snippet(cap, format!("{local_name}<$0>"));
+ .insert_snippet(cap, format!("{}<$0>", local_name.display(db)));
}
}
}
@@ -348,6 +370,8 @@ fn render_resolution_simple_(
if let Some(import_to_add) = ctx.import_to_add {
item.add_import(import_to_add);
}
+
+ item.doc_aliases(ctx.doc_aliases);
item
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index 70b19988c..3c73983c3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
}
item.insert_text(escaped_name);
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 197592e78..8afce8db5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -52,8 +52,13 @@ fn render(
let (call, escaped_call) = match &func_kind {
FuncKind::Method(_, Some(receiver)) => (
- format!("{}.{}", receiver.unescaped(), name.unescaped()).into(),
- format!("{receiver}.{name}").into(),
+ format!(
+ "{}.{}",
+ receiver.unescaped().display(ctx.db()),
+ name.unescaped().display(ctx.db())
+ )
+ .into(),
+ format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(),
),
_ => (name.unescaped().to_smol_str(), name.to_smol_str()),
};
@@ -147,6 +152,8 @@ fn render(
}
}
}
+
+ item.doc_aliases(ctx.doc_aliases);
item
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index ed78fcd8e..728d236df 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -71,8 +71,10 @@ fn render(
}
None => (name.clone().into(), name.into(), false),
};
- let (qualified_name, escaped_qualified_name) =
- (qualified_name.unescaped().to_string(), qualified_name.to_string());
+ let (qualified_name, escaped_qualified_name) = (
+ qualified_name.unescaped().display(ctx.db()).to_string(),
+ qualified_name.display(ctx.db()).to_string(),
+ );
let snippet_cap = ctx.snippet_cap();
let mut rendered = match kind {
@@ -98,7 +100,7 @@ fn render(
}
let label = format_literal_label(&qualified_name, kind, snippet_cap);
let lookup = if qualified {
- format_literal_lookup(&short_qualified_name.to_string(), kind)
+ format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
} else {
format_literal_lookup(&qualified_name, kind)
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index 44e886076..ce7af1d34 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -74,7 +74,7 @@ fn render(
item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name));
}
_ => {
- cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
+ cov_mark::hit!(dont_insert_macro_call_parens_unnecessary);
item.insert_text(escaped_name);
}
};
@@ -140,8 +140,8 @@ mod tests {
use crate::tests::check_edit;
#[test]
- fn dont_insert_macro_call_parens_unncessary() {
- cov_mark::check!(dont_insert_macro_call_parens_unncessary);
+ fn dont_insert_macro_call_parens_unnecessary() {
+ cov_mark::check!(dont_insert_macro_call_parens_unnecessary);
check_edit(
"frobnicate",
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index 9225c91be..d06abc5e9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat(
let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
let (name, escaped_name) = match path {
- Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
+ Some(path) => (
+ path.unescaped().display(ctx.db()).to_string().into(),
+ path.display(ctx.db()).to_string().into(),
+ ),
None => {
let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
(name.unescaped().to_smol_str(), name.to_smol_str())
@@ -121,7 +124,7 @@ fn build_completion(
Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
None => item.insert_text(pat),
};
- item.build()
+ item.build(ctx.db())
}
fn render_pat(
@@ -172,7 +175,7 @@ fn render_record_as_pat(
format!(
"{name} {{ {}{} }}",
fields.enumerate().format_with(", ", |(idx, field), f| {
- f(&format_args!("{}${}", field.name(db), idx + 1))
+ f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1))
}),
if fields_omitted { ", .." } else { "" },
name = name
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index fbe120d2a..343ba7e28 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -53,5 +53,5 @@ fn render(
}
item.insert_text(escaped_name);
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index 6e0c53ec9..93e943dbe 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -21,8 +21,10 @@ pub(crate) fn render_union_literal(
let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
let (qualified_name, escaped_qualified_name) = match path {
- Some(p) => (p.unescaped().to_string(), p.to_string()),
- None => (name.unescaped().to_string(), name.to_string()),
+ Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()),
+ None => {
+ (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string())
+ }
};
let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap());
let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record);
@@ -51,9 +53,9 @@ pub(crate) fn render_union_literal(
format!(
"{} {{ {} }}",
escaped_qualified_name,
- fields
- .iter()
- .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) })
+ fields.iter().format_with(", ", |field, f| {
+ f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db())))
+ })
)
};
@@ -61,7 +63,11 @@ pub(crate) fn render_union_literal(
"{} {{ {}{} }}",
qualified_name,
fields.iter().format_with(", ", |field, f| {
- f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db())))
+ f(&format_args!(
+ "{}: {}",
+ field.name(ctx.db()).display(ctx.db()),
+ field.ty(ctx.db()).display(ctx.db())
+ ))
}),
if fields_omitted { ", .." } else { "" }
);
@@ -76,5 +82,5 @@ pub(crate) fn render_union_literal(
None => item.insert_text(literal),
};
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index 55c55725b..a9a01a3a3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -27,14 +27,14 @@ pub(crate) fn render_record_lit(
}
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
if snippet_cap.is_some() {
- f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
+ f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1))
} else {
- f(&format_args!("{}: ()", field.name(db)))
+ f(&format_args!("{}: ()", field.name(db).display(db.upcast())))
}
});
let types = fields.iter().format_with(", ", |field, f| {
- f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
+ f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db)))
});
RenderedLiteral {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 1fe48b9e9..2464e8d5f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -23,7 +23,8 @@ mod type_pos;
mod use_tree;
mod visibility;
-use hir::{db::DefDatabase, PrefixKind};
+use expect_test::Expect;
+use hir::PrefixKind;
use ide_db::{
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -104,7 +105,7 @@ fn completion_list_with_config(
include_keywords: bool,
trigger_character: Option<char>,
) -> String {
- // filter out all but one builtintype completion for smaller test outputs
+ // filter out all but one built-in type completion for smaller test outputs
let items = get_all_items(config, ra_fixture, trigger_character);
let items = items
.into_iter()
@@ -120,7 +121,7 @@ fn completion_list_with_config(
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
let mut database = RootDatabase::default();
- database.set_enable_proc_attr_macros(true);
+ database.enable_proc_attr_macros();
database.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -197,11 +198,11 @@ pub(crate) fn check_edit_with_config(
&db,
&config,
position,
- completion.import_to_add.iter().filter_map(|import_edit| {
- let import_path = &import_edit.import_path;
- let import_name = import_path.segments().last()?;
- Some((import_path.to_string(), import_name.to_string()))
- }),
+ completion
+ .import_to_add
+ .iter()
+ .cloned()
+ .filter_map(|(import_path, import_name)| Some((import_path, import_name))),
)
.into_iter()
.flatten()
@@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
assert_eq_text!(&ra_fixture_after, &actual)
}
+fn check_empty(ra_fixture: &str, expect: Expect) {
+ let actual = completion_list(ra_fixture);
+ expect.assert_eq(&actual);
+}
+
pub(crate) fn get_all_items(
config: CompletionConfig,
code: &str,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index c1c6a689e..be5b7f8a3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1,18 +1,13 @@
//! Completion tests for expressions.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
expect.assert_eq(&actual)
}
-fn check_empty(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
-}
-
#[test]
fn complete_literal_struct_with_a_private_field() {
// `FooDesc.bar` is private, the completion should not be triggered.
@@ -672,7 +667,7 @@ fn main() {
}
#[test]
-fn varaiant_with_struct() {
+fn variant_with_struct() {
check_empty(
r#"
pub struct YoloVariant {
@@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
"#]],
);
}
+
+#[test]
+fn expr_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableThisShouldNotBeListed;
+"#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn expr_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableButWeAreOnNightlyAnyway;
+"#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ st UnstableButWeAreOnNightlyAnyway
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 0b485eb77..8c038c0fb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -1108,6 +1108,41 @@ fn function() {
}
#[test]
+fn flyimport_pattern_no_unstable_item_on_stable() {
+ check(
+ r#"
+//- /main.rs crate:main deps:std
+fn function() {
+ let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+ expect![""],
+ );
+}
+
+#[test]
+fn flyimport_pattern_unstable_item_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn function() {
+ let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+ expect![[r#"
+ st FooStruct (use std::FooStruct)
+ "#]],
+ );
+}
+
+#[test]
fn flyimport_item_name() {
check(
r#"
@@ -1230,3 +1265,24 @@ macro_rules! define_struct {
"#]],
);
}
+
+#[test]
+fn macro_use_prelude_is_in_scope() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+#[macro_use]
+extern crate dep;
+
+fn main() {
+ print$0
+}
+//- /lib.rs crate:dep
+#[macro_export]
+macro_rules! println {
+ () => {}
+}
+"#,
+ expect![""],
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 9fc731bb1..2b5b4dd77 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -1,7 +1,7 @@
//! Completion tests for item list position.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
@@ -298,6 +298,58 @@ impl Test for () {
}
#[test]
+fn in_trait_impl_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+trait Test {
+ #[unstable]
+ type Type;
+ #[unstable]
+ const CONST: ();
+ #[unstable]
+ fn function();
+}
+
+impl Test for () {
+ $0
+}
+"#,
+ expect![[r#"
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
+fn in_trait_impl_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+trait Test {
+ #[unstable]
+ type Type;
+ #[unstable]
+ const CONST: ();
+ #[unstable]
+ fn function();
+}
+
+impl Test for () {
+ $0
+}
+"#,
+ expect![[r#"
+ ct const CONST: () =
+ fn fn function()
+ ta type Type =
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
fn after_unit_struct() {
check(
r#"struct S; f$0"#,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index c0e485c36..8af6cce98 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -1,12 +1,7 @@
//! Completion tests for pattern position.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check_empty(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -742,3 +737,56 @@ fn f(x: EnumAlias<u8>) {
"#]],
);
}
+
+#[test]
+fn pat_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+ let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+ Variant
+}
+"#,
+ expect![[r#"
+ md std
+ kw mut
+ kw ref
+ "#]],
+ );
+}
+
+#[test]
+fn pat_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+ let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+ Variant
+}
+"#,
+ expect![[r#"
+ en Enum
+ md std
+ st S
+ kw mut
+ kw ref
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index 2656a4d54..789ad6634 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -1,7 +1,7 @@
//! Completion tests for predicates and bounds.
use expect_test::{expect, Expect};
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -129,3 +129,43 @@ impl Record {
"#]],
);
}
+
+#[test]
+fn pred_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+ expect![[r#"
+ md std
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
+fn pred_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+ expect![[r#"
+ md std
+ tt Trait
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
index 92ea4d15b..2d6234e31 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
@@ -81,7 +81,7 @@ impl Foo {
}
#[proc_macros::input_replace(
- fn suprise() {
+ fn surprise() {
Foo.$0
}
)]
@@ -114,7 +114,7 @@ impl Foo {
}
#[proc_macros::input_replace(
- fn suprise() {
+ fn surprise() {
Foo.f$0
}
)]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
index f8a6f6cd3..382472083 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
@@ -989,3 +989,294 @@ fn foo { crate::::$0 }
expect![""],
)
}
+
+#[test]
+fn completes_struct_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+#[doc(alias = "Bar")]
+struct Foo;
+
+fn here_we_go() {
+ $0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ st Foo (alias Bar)
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_via_multiple_doc_aliases_in_fn_body() {
+ check(
+ r#"
+#[doc(alias("Bar", "Qux"))]
+#[doc(alias = "Baz")]
+struct Foo;
+
+fn here_we_go() {
+ B$0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ st Foo (alias Bar, Qux, Baz)
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_field_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo {
+ #[doc(alias = "qux")]
+ bar: u8
+};
+
+fn here_we_go() {
+ let foo = Foo { q$0 }
+}
+"#,
+ expect![[r#"
+ fd bar (alias qux) u8
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_fn_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo;
+impl Foo {
+ #[doc(alias = "qux")]
+ fn bar() -> u8 { 1 }
+}
+
+fn here_we_go() {
+ Foo::q$0
+}
+"#,
+ expect![[r#"
+ fn bar() (alias qux) fn() -> u8
+ "#]],
+ );
+}
+
+#[test]
+fn completes_method_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo {
+ bar: u8
+}
+impl Foo {
+ #[doc(alias = "qux")]
+ fn baz(&self) -> u8 {
+ self.bar
+ }
+}
+
+fn here_we_go() {
+ let foo = Foo { field: 42 };
+ foo.q$0
+}
+"#,
+ expect![[r#"
+ fd bar u8
+ me baz() (alias qux) fn(&self) -> u8
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
+ sn unsafe unsafe {}
+ "#]],
+ );
+}
+
+#[test]
+fn completes_fn_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+#[doc(alias = "qux")]
+fn foo() {}
+fn bar() { qu$0 }
+"#,
+ expect![[r#"
+ fn bar() fn()
+ fn foo() (alias qux) fn()
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_name_via_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar(u8);
+}
+
+fn here_we_go() {
+ use foo;
+ let foo = foo::Q$0
+}
+"#,
+ expect![[r#"
+ st Bar (alias Qux)
+ "#]],
+ );
+}
+
+#[test]
+fn completes_use_via_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar(u8);
+}
+
+fn here_we_go() {
+ use foo::Q$0;
+}
+"#,
+ expect![[r#"
+ st Bar (alias Qux)
+ "#]],
+ );
+}
+
+#[test]
+fn completes_flyimport_with_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar();
+}
+
+fn here_we_go() {
+ let foo = Bar$0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ md foo
+ st Bar (alias Qux) (use foo::Bar)
+ bt u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index c3f4fb4d1..8cb1ff4a1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -1,7 +1,7 @@
//! Completion tests for type position.
use expect_test::{expect, Expect};
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -669,3 +669,53 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#]],
);
}
+
+#[test]
+fn type_pos_no_unstable_type_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+ f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+ expect![[r#"
+ md std
+ sp Self
+ st Foo
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ )
+}
+
+#[test]
+fn type_pos_unstable_type_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+ f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+ expect![[r#"
+ md std
+ sp Self
+ st Foo
+ st S
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
index 037d7dce5..4c74dba52 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
@@ -382,3 +382,50 @@ use self::foo::impl$0
"#]],
);
}
+
+#[test]
+fn use_tree_no_unstable_items_on_stable() {
+ check(
+ r#"
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+ expect![""],
+ );
+}
+
+#[test]
+fn use_tree_unstable_items_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+ expect![[r#"
+ fn foo fn()
+ md simd
+ st S
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 57daaf623..4e75dc4db 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -23,6 +23,8 @@ itertools = "0.10.5"
arrayvec = "0.7.2"
indexmap = "1.9.1"
memchr = "2.5.0"
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
base-db.workspace = true
@@ -36,6 +38,8 @@ text-edit.workspace = true
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
+line-index.workspace = true
+
[dev-dependencies]
expect-test = "1.4.0"
oorandom = "11.1.3"
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index ea1d9cc49..0dd544d0a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -1,13 +1,15 @@
//! Applies changes to the IDE state transactionally.
-use std::sync::Arc;
-
use base_db::{
- salsa::{Database, Durability},
+ salsa::{
+ debug::{DebugQueryTable, TableEntry},
+ Database, Durability, Query, QueryTable,
+ },
Change, SourceRootId,
};
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use crate::{symbol_index::SymbolsDatabase, RootDatabase};
@@ -48,22 +50,44 @@ impl RootDatabase {
// | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
// |===
// image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
- pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
- let mut acc: Vec<(String, Bytes)> = vec![];
+ pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
+ let mut acc: Vec<(String, Bytes, usize)> = vec![];
+
+ fn collect_query_count<'q, Q>(table: &QueryTable<'q, Q>) -> usize
+ where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: Query,
+ <Q as Query>::Storage: 'q,
+ {
+ struct EntryCounter(usize);
+ impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+ fn from_iter<T>(iter: T) -> EntryCounter
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ EntryCounter(iter.into_iter().count())
+ }
+ }
+ table.entries::<EntryCounter>().0
+ }
+
macro_rules! purge_each_query {
($($q:path)*) => {$(
let before = memory_usage().allocated;
- $q.in_db(self).purge();
+ let table = $q.in_db(self);
+ let count = collect_query_count(&table);
+ table.purge();
let after = memory_usage().allocated;
let q: $q = Default::default();
let name = format!("{:?}", q);
- acc.push((name, before - after));
+ acc.push((name, before - after, count));
)*}
}
purge_each_query![
// SourceDatabase
base_db::ParseQuery
base_db::CrateGraphQuery
+ base_db::ProcMacrosQuery
// SourceDatabaseExt
base_db::FileTextQuery
@@ -79,7 +103,6 @@ impl RootDatabase {
hir::db::MacroDefQuery
hir::db::MacroExpandQuery
hir::db::ExpandProcMacroQuery
- hir::db::MacroExpandErrorQuery
hir::db::HygieneFrameQuery
// DefDatabase
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
index 8c6c1c44a..7a7328f31 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
@@ -98,7 +98,7 @@ impl FromStr for AssistKind {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str, pub AssistKind);
-/// A way to control how many asssist to resolve during the assist resolution.
+/// A way to control how many assist to resolve during the assist resolution.
/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
#[derive(Debug)]
pub enum AssistResolveStrategy {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 4071c490b..760834bfa 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -8,8 +8,8 @@
use arrayvec::ArrayVec;
use hir::{
Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
- Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
- Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
+ Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name,
+ PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
Visibility,
};
use stdx::impl_from;
@@ -622,22 +622,3 @@ impl From<ModuleDef> for Definition {
}
}
}
-
-impl From<Definition> for Option<ItemInNs> {
- fn from(def: Definition) -> Self {
- let item = match def {
- Definition::Module(it) => ModuleDef::Module(it),
- Definition::Function(it) => ModuleDef::Function(it),
- Definition::Adt(it) => ModuleDef::Adt(it),
- Definition::Variant(it) => ModuleDef::Variant(it),
- Definition::Const(it) => ModuleDef::Const(it),
- Definition::Static(it) => ModuleDef::Static(it),
- Definition::Trait(it) => ModuleDef::Trait(it),
- Definition::TraitAlias(it) => ModuleDef::TraitAlias(it),
- Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
- Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
- _ => return None,
- };
- Some(ItemInNs::from(item))
- }
-}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index f0c369096..e488300b4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -4230,7 +4230,7 @@ pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.
pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };
```
-Like transarent `struct`s, a transparent `union` of type `U` has the same
+Like transparent `struct`s, a transparent `union` of type `U` has the same
layout, size, and ABI as its single non-ZST field. If it is generic over a type
`T`, and all its fields are ZSTs except for exactly one field of type `T`, then
it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
@@ -6548,7 +6548,7 @@ subtracting elements in an Add impl."##,
},
Lint {
label: "clippy::suspicious_assignment_formatting",
- description: r##"Checks for use of the non-existent `=*`, `=!` and `=-`
+ description: r##"Checks for use of the nonexistent `=*`, `=!` and `=-`
operators."##,
},
Lint {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index 8e3b1eef1..eba9d8afc 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -77,7 +77,7 @@ pub fn visit_file_defs(
}
module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
- let is_root = module.is_crate_root(db);
+ let is_root = module.is_crate_root();
module
.legacy_macros(db)
.into_iter()
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index b26b0a908..901d592c6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -362,12 +362,12 @@ fn import_for_item(
let original_item_candidate = item_for_path_search(db, original_item)?;
let import_path_candidate = mod_path(original_item_candidate)?;
- let import_path_string = import_path_candidate.to_string();
+ let import_path_string = import_path_candidate.display(db).to_string();
let expected_import_end = if item_as_assoc(db, original_item).is_some() {
unresolved_qualifier.to_string()
} else {
- format!("{unresolved_qualifier}::{}", item_name(db, original_item)?)
+ format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db))
};
if !import_path_string.contains(unresolved_first_segment)
|| !import_path_string.ends_with(&expected_import_end)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 07a57c883..46f1353e2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -5,17 +5,11 @@
use either::Either;
use hir::{
import_map::{self, ImportKind},
- symbols::FileSymbol,
AsAssocItem, Crate, ItemInNs, Semantics,
};
use limit::Limit;
-use syntax::{ast, AstNode, SyntaxKind::NAME};
-use crate::{
- defs::{Definition, NameClass},
- imports::import_assets::NameToImport,
- symbol_index, RootDatabase,
-};
+use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
/// A value to use, when uncertain which limit to pick.
pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
@@ -115,12 +109,12 @@ fn find_items<'a>(
});
// Query the local crate using the symbol index.
- let local_results = symbol_index::crate_symbols(db, krate, local_query)
+ let local_results = local_query
+ .search(&symbol_index::crate_symbols(db, krate))
.into_iter()
- .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
- .filter_map(|name_definition_to_import| match name_definition_to_import {
- Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
- def => <Option<_>>::from(def),
+ .filter_map(|local_candidate| match local_candidate.def {
+ hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)),
+ def => Some(ItemInNs::from(def)),
});
external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
@@ -130,22 +124,6 @@ fn find_items<'a>(
})
}
-fn get_name_definition(
- sema: &Semantics<'_, RootDatabase>,
- import_candidate: &FileSymbol,
-) -> Option<Definition> {
- let _p = profile::span("get_name_definition");
-
- let candidate_node = import_candidate.loc.syntax(sema)?;
- let candidate_name_node = if candidate_node.kind() != NAME {
- candidate_node.children().find(|it| it.kind() == NAME)?
- } else {
- candidate_node
- };
- let name = ast::Name::cast(candidate_name_node)?;
- NameClass::classify(sema, &name)?.defined()
-}
-
fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some()
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index b1df11bf9..ff1a20f03 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -13,7 +13,6 @@ pub mod famous_defs;
pub mod helpers;
pub mod items_locator;
pub mod label;
-pub mod line_index;
pub mod path_transform;
pub mod rename;
pub mod rust_doc;
@@ -43,21 +42,20 @@ pub mod syntax_helpers {
pub use parser::LexedStr;
}
-use std::{fmt, mem::ManuallyDrop, sync::Arc};
+use std::{fmt, mem::ManuallyDrop};
use base_db::{
salsa::{self, Durability},
AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
};
-use hir::{
- db::{DefDatabase, ExpandDatabase, HirDatabase},
- symbols::FileSymbolKind,
-};
-use stdx::hash::NoHashHashSet;
+use hir::db::{DefDatabase, ExpandDatabase, HirDatabase};
+use triomphe::Arc;
use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
+pub use ::line_index;
+
/// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience.
pub use base_db;
@@ -114,13 +112,13 @@ impl Upcast<dyn HirDatabase> for RootDatabase {
}
impl FileLoader for RootDatabase {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -137,18 +135,186 @@ impl RootDatabase {
pub fn new(lru_capacity: Option<usize>) -> RootDatabase {
let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) };
db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
+ db.set_proc_macros_with_durability(Default::default(), Durability::HIGH);
db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
- db.set_enable_proc_attr_macros(false);
- db.update_lru_capacity(lru_capacity);
+ db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH);
+ db.update_parse_query_lru_capacity(lru_capacity);
db
}
- pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
- let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
+ pub fn enable_proc_attr_macros(&mut self) {
+ self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
+ }
+
+ pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option<usize>) {
+ let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP);
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
- hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
- hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
+ // macro expansions are usually rather small, so we can afford to keep more of them alive
+ hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
+ hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
+ }
+
+ pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
+ use hir::db as hir_db;
+
+ base_db::ParseQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(ParseQuery))
+ .copied()
+ .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+ hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(ParseMacroExpansionQuery))
+ .copied()
+ .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+ hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(MacroExpandQuery))
+ .copied()
+ .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+
+ macro_rules! update_lru_capacity_per_query {
+ ($( $module:ident :: $query:ident )*) => {$(
+ if let Some(&cap) = lru_capacities.get(stringify!($query)) {
+ $module::$query.in_db_mut(self).set_lru_capacity(cap);
+ }
+ )*}
+ }
+ update_lru_capacity_per_query![
+ // SourceDatabase
+ // base_db::ParseQuery
+ // base_db::CrateGraphQuery
+ // base_db::ProcMacrosQuery
+
+ // SourceDatabaseExt
+ // base_db::FileTextQuery
+ // base_db::FileSourceRootQuery
+ // base_db::SourceRootQuery
+ base_db::SourceRootCratesQuery
+
+ // ExpandDatabase
+ hir_db::AstIdMapQuery
+ // hir_db::ParseMacroExpansionQuery
+ // hir_db::InternMacroCallQuery
+ hir_db::MacroArgTextQuery
+ hir_db::MacroDefQuery
+ // hir_db::MacroExpandQuery
+ hir_db::ExpandProcMacroQuery
+ hir_db::HygieneFrameQuery
+ hir_db::ParseMacroExpansionErrorQuery
+
+ // DefDatabase
+ hir_db::FileItemTreeQuery
+ hir_db::CrateDefMapQueryQuery
+ hir_db::BlockDefMapQuery
+ hir_db::StructDataQuery
+ hir_db::StructDataWithDiagnosticsQuery
+ hir_db::UnionDataQuery
+ hir_db::UnionDataWithDiagnosticsQuery
+ hir_db::EnumDataQuery
+ hir_db::EnumDataWithDiagnosticsQuery
+ hir_db::ImplDataQuery
+ hir_db::ImplDataWithDiagnosticsQuery
+ hir_db::TraitDataQuery
+ hir_db::TraitDataWithDiagnosticsQuery
+ hir_db::TraitAliasDataQuery
+ hir_db::TypeAliasDataQuery
+ hir_db::FunctionDataQuery
+ hir_db::ConstDataQuery
+ hir_db::StaticDataQuery
+ hir_db::Macro2DataQuery
+ hir_db::MacroRulesDataQuery
+ hir_db::ProcMacroDataQuery
+ hir_db::BodyWithSourceMapQuery
+ hir_db::BodyQuery
+ hir_db::ExprScopesQuery
+ hir_db::GenericParamsQuery
+ hir_db::VariantsAttrsQuery
+ hir_db::FieldsAttrsQuery
+ hir_db::VariantsAttrsSourceMapQuery
+ hir_db::FieldsAttrsSourceMapQuery
+ hir_db::AttrsQuery
+ hir_db::CrateLangItemsQuery
+ hir_db::LangItemQuery
+ hir_db::ImportMapQuery
+ hir_db::FieldVisibilitiesQuery
+ hir_db::FunctionVisibilityQuery
+ hir_db::ConstVisibilityQuery
+ hir_db::CrateSupportsNoStdQuery
+
+ // HirDatabase
+ hir_db::InferQueryQuery
+ hir_db::MirBodyQuery
+ hir_db::BorrowckQuery
+ hir_db::TyQuery
+ hir_db::ValueTyQuery
+ hir_db::ImplSelfTyQuery
+ hir_db::ConstParamTyQuery
+ hir_db::ConstEvalQuery
+ hir_db::ConstEvalDiscriminantQuery
+ hir_db::ImplTraitQuery
+ hir_db::FieldTypesQuery
+ hir_db::LayoutOfAdtQuery
+ hir_db::TargetDataLayoutQuery
+ hir_db::CallableItemSignatureQuery
+ hir_db::ReturnTypeImplTraitsQuery
+ hir_db::GenericPredicatesForParamQuery
+ hir_db::GenericPredicatesQuery
+ hir_db::TraitEnvironmentQuery
+ hir_db::GenericDefaultsQuery
+ hir_db::InherentImplsInCrateQuery
+ hir_db::InherentImplsInBlockQuery
+ hir_db::IncoherentInherentImplCratesQuery
+ hir_db::TraitImplsInCrateQuery
+ hir_db::TraitImplsInBlockQuery
+ hir_db::TraitImplsInDepsQuery
+ // hir_db::InternCallableDefQuery
+ // hir_db::InternLifetimeParamIdQuery
+ // hir_db::InternImplTraitIdQuery
+ // hir_db::InternTypeOrConstParamIdQuery
+ // hir_db::InternClosureQuery
+ // hir_db::InternGeneratorQuery
+ hir_db::AssociatedTyDataQuery
+ hir_db::TraitDatumQuery
+ hir_db::StructDatumQuery
+ hir_db::ImplDatumQuery
+ hir_db::FnDefDatumQuery
+ hir_db::FnDefVarianceQuery
+ hir_db::AdtVarianceQuery
+ hir_db::AssociatedTyValueQuery
+ hir_db::TraitSolveQueryQuery
+ hir_db::ProgramClausesForChalkEnvQuery
+
+ // SymbolsDatabase
+ symbol_index::ModuleSymbolsQuery
+ symbol_index::LibrarySymbolsQuery
+ // symbol_index::LocalRootsQuery
+ // symbol_index::LibraryRootsQuery
+
+ // LineIndexDatabase
+ crate::LineIndexQuery
+
+ // InternDatabase
+ // hir_db::InternFunctionQuery
+ // hir_db::InternStructQuery
+ // hir_db::InternUnionQuery
+ // hir_db::InternEnumQuery
+ // hir_db::InternConstQuery
+ // hir_db::InternStaticQuery
+ // hir_db::InternTraitQuery
+ // hir_db::InternTraitAliasQuery
+ // hir_db::InternTypeAliasQuery
+ // hir_db::InternImplQuery
+ // hir_db::InternExternBlockQuery
+ // hir_db::InternBlockQuery
+ // hir_db::InternMacro2Query
+ // hir_db::InternProcMacroQuery
+ // hir_db::InternMacroRulesQuery
+ ];
}
}
@@ -211,20 +377,22 @@ impl From<hir::MacroKind> for SymbolKind {
}
}
-impl From<FileSymbolKind> for SymbolKind {
- fn from(it: FileSymbolKind) -> Self {
+impl From<hir::ModuleDefId> for SymbolKind {
+ fn from(it: hir::ModuleDefId) -> Self {
match it {
- FileSymbolKind::Const => SymbolKind::Const,
- FileSymbolKind::Enum => SymbolKind::Enum,
- FileSymbolKind::Function => SymbolKind::Function,
- FileSymbolKind::Macro => SymbolKind::Macro,
- FileSymbolKind::Module => SymbolKind::Module,
- FileSymbolKind::Static => SymbolKind::Static,
- FileSymbolKind::Struct => SymbolKind::Struct,
- FileSymbolKind::Trait => SymbolKind::Trait,
- FileSymbolKind::TraitAlias => SymbolKind::TraitAlias,
- FileSymbolKind::TypeAlias => SymbolKind::TypeAlias,
- FileSymbolKind::Union => SymbolKind::Union,
+ hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
+ hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
+ hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
+ hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
+ hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
+ hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
+ hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct,
+ hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum,
+ hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union,
+ hir::ModuleDefId::TraitId(..) => SymbolKind::Trait,
+ hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias,
+ hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias,
+ hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias,
}
}
}
@@ -247,4 +415,5 @@ impl SnippetCap {
#[cfg(test)]
mod tests {
mod sourcegen_lints;
+ mod line_index;
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
deleted file mode 100644
index 16814a1e6..000000000
--- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
+++ /dev/null
@@ -1,314 +0,0 @@
-//! `LineIndex` maps flat `TextSize` offsets into `(Line, Column)`
-//! representation.
-use std::{iter, mem};
-
-use stdx::hash::NoHashHashMap;
-use syntax::{TextRange, TextSize};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct LineIndex {
- /// Offset the beginning of each line, zero-based.
- pub(crate) newlines: Vec<TextSize>,
- /// List of non-ASCII characters on each line.
- pub(crate) line_wide_chars: NoHashHashMap<u32, Vec<WideChar>>,
-}
-
-/// Line/Column information in native, utf8 format.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct LineCol {
- /// Zero-based
- pub line: u32,
- /// Zero-based utf8 offset
- pub col: u32,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum WideEncoding {
- Utf16,
- Utf32,
-}
-
-/// Line/Column information in legacy encodings.
-///
-/// Deliberately not a generic type and different from `LineCol`.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct WideLineCol {
- /// Zero-based
- pub line: u32,
- /// Zero-based
- pub col: u32,
-}
-
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub(crate) struct WideChar {
- /// Start offset of a character inside a line, zero-based
- pub(crate) start: TextSize,
- /// End offset of a character inside a line, zero-based
- pub(crate) end: TextSize,
-}
-
-impl WideChar {
- /// Returns the length in 8-bit UTF-8 code units.
- fn len(&self) -> TextSize {
- self.end - self.start
- }
-
- /// Returns the length in UTF-16 or UTF-32 code units.
- fn wide_len(&self, enc: WideEncoding) -> usize {
- match enc {
- WideEncoding::Utf16 => {
- if self.len() == TextSize::from(4) {
- 2
- } else {
- 1
- }
- }
-
- WideEncoding::Utf32 => 1,
- }
- }
-}
-
-impl LineIndex {
- pub fn new(text: &str) -> LineIndex {
- let mut line_wide_chars = NoHashHashMap::default();
- let mut wide_chars = Vec::new();
-
- let mut newlines = Vec::with_capacity(16);
- newlines.push(TextSize::from(0));
-
- let mut curr_row = 0.into();
- let mut curr_col = 0.into();
- let mut line = 0;
- for c in text.chars() {
- let c_len = TextSize::of(c);
- curr_row += c_len;
- if c == '\n' {
- newlines.push(curr_row);
-
- // Save any utf-16 characters seen in the previous line
- if !wide_chars.is_empty() {
- line_wide_chars.insert(line, mem::take(&mut wide_chars));
- }
-
- // Prepare for processing the next line
- curr_col = 0.into();
- line += 1;
- continue;
- }
-
- if !c.is_ascii() {
- wide_chars.push(WideChar { start: curr_col, end: curr_col + c_len });
- }
-
- curr_col += c_len;
- }
-
- // Save any utf-16 characters seen in the last line
- if !wide_chars.is_empty() {
- line_wide_chars.insert(line, wide_chars);
- }
-
- LineIndex { newlines, line_wide_chars }
- }
-
- pub fn line_col(&self, offset: TextSize) -> LineCol {
- let line = self.newlines.partition_point(|&it| it <= offset) - 1;
- let line_start_offset = self.newlines[line];
- let col = offset - line_start_offset;
- LineCol { line: line as u32, col: col.into() }
- }
-
- pub fn offset(&self, line_col: LineCol) -> Option<TextSize> {
- self.newlines
- .get(line_col.line as usize)
- .map(|offset| offset + TextSize::from(line_col.col))
- }
-
- pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> WideLineCol {
- let col = self.utf8_to_wide_col(enc, line_col.line, line_col.col.into());
- WideLineCol { line: line_col.line, col: col as u32 }
- }
-
- pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> LineCol {
- let col = self.wide_to_utf8_col(enc, line_col.line, line_col.col);
- LineCol { line: line_col.line, col: col.into() }
- }
-
- pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
- let lo = self.newlines.partition_point(|&it| it < range.start());
- let hi = self.newlines.partition_point(|&it| it <= range.end());
- let all = iter::once(range.start())
- .chain(self.newlines[lo..hi].iter().copied())
- .chain(iter::once(range.end()));
-
- all.clone()
- .zip(all.skip(1))
- .map(|(lo, hi)| TextRange::new(lo, hi))
- .filter(|it| !it.is_empty())
- }
-
- fn utf8_to_wide_col(&self, enc: WideEncoding, line: u32, col: TextSize) -> usize {
- let mut res: usize = col.into();
- if let Some(wide_chars) = self.line_wide_chars.get(&line) {
- for c in wide_chars {
- if c.end <= col {
- res -= usize::from(c.len()) - c.wide_len(enc);
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
- res
- }
-
- fn wide_to_utf8_col(&self, enc: WideEncoding, line: u32, mut col: u32) -> TextSize {
- if let Some(wide_chars) = self.line_wide_chars.get(&line) {
- for c in wide_chars {
- if col > u32::from(c.start) {
- col += u32::from(c.len()) - c.wide_len(enc) as u32;
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
-
- col.into()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use test_utils::skip_slow_tests;
-
- use super::WideEncoding::{Utf16, Utf32};
- use super::*;
-
- #[test]
- fn test_line_index() {
- let text = "hello\nworld";
- let table = [
- (00, 0, 0),
- (01, 0, 1),
- (05, 0, 5),
- (06, 1, 0),
- (07, 1, 1),
- (08, 1, 2),
- (10, 1, 4),
- (11, 1, 5),
- (12, 1, 6),
- ];
-
- let index = LineIndex::new(text);
- for (offset, line, col) in table {
- assert_eq!(index.line_col(offset.into()), LineCol { line, col });
- }
-
- let text = "\nhello\nworld";
- let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
- let index = LineIndex::new(text);
- for (offset, line, col) in table {
- assert_eq!(index.line_col(offset.into()), LineCol { line, col });
- }
- }
-
- #[test]
- fn test_char_len() {
- assert_eq!('メ'.len_utf8(), 3);
- assert_eq!('メ'.len_utf16(), 1);
- }
-
- #[test]
- fn test_empty_index() {
- let col_index = LineIndex::new(
- "
-const C: char = 'x';
-",
- );
- assert_eq!(col_index.line_wide_chars.len(), 0);
- }
-
- #[test]
- fn test_every_chars() {
- if skip_slow_tests() {
- return;
- }
-
- let text: String = {
- let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat!
- chars.extend("\n".repeat(chars.len() / 16).chars());
- let mut rng = oorandom::Rand32::new(stdx::rand::seed());
- stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize);
- chars.into_iter().collect()
- };
- assert!(text.contains('💩')); // Sanity check.
-
- let line_index = LineIndex::new(&text);
-
- let mut lin_col = LineCol { line: 0, col: 0 };
- let mut col_utf16 = 0;
- let mut col_utf32 = 0;
- for (offset, c) in text.char_indices() {
- let got_offset = line_index.offset(lin_col).unwrap();
- assert_eq!(usize::from(got_offset), offset);
-
- let got_lin_col = line_index.line_col(got_offset);
- assert_eq!(got_lin_col, lin_col);
-
- for enc in [Utf16, Utf32] {
- let wide_lin_col = line_index.to_wide(enc, lin_col);
- let got_lin_col = line_index.to_utf8(enc, wide_lin_col);
- assert_eq!(got_lin_col, lin_col);
-
- let want_col = match enc {
- Utf16 => col_utf16,
- Utf32 => col_utf32,
- };
- assert_eq!(wide_lin_col.col, want_col)
- }
-
- if c == '\n' {
- lin_col.line += 1;
- lin_col.col = 0;
- col_utf16 = 0;
- col_utf32 = 0;
- } else {
- lin_col.col += c.len_utf8() as u32;
- col_utf16 += c.len_utf16() as u32;
- col_utf32 += 1;
- }
- }
- }
-
- #[test]
- fn test_splitlines() {
- fn r(lo: u32, hi: u32) -> TextRange {
- TextRange::new(lo.into(), hi.into())
- }
-
- let text = "a\nbb\nccc\n";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
- let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
- assert_eq!(actual, expected);
-
- let text = "";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
- let expected = vec![];
- assert_eq!(actual, expected);
-
- let text = "\n";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
- let expected = vec![r(0, 1)];
- assert_eq!(actual, expected)
- }
-}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 6402a84a6..73e6a920e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -9,6 +9,19 @@ use syntax::{
ted, SyntaxNode,
};
+#[derive(Default)]
+struct AstSubsts {
+ types_and_consts: Vec<TypeOrConst>,
+ lifetimes: Vec<ast::LifetimeArg>,
+}
+
+enum TypeOrConst {
+ Either(ast::TypeArg), // indistinguishable type or const param
+ Const(ast::ConstArg),
+}
+
+type LifetimeName = String;
+
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
///
/// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +47,7 @@ use syntax::{
/// ```
pub struct PathTransform<'a> {
generic_def: Option<hir::GenericDef>,
- substs: Vec<ast::Type>,
+ substs: AstSubsts,
target_scope: &'a SemanticsScope<'a>,
source_scope: &'a SemanticsScope<'a>,
}
@@ -72,7 +85,12 @@ impl<'a> PathTransform<'a> {
target_scope: &'a SemanticsScope<'a>,
source_scope: &'a SemanticsScope<'a>,
) -> PathTransform<'a> {
- PathTransform { source_scope, target_scope, generic_def: None, substs: Vec::new() }
+ PathTransform {
+ source_scope,
+ target_scope,
+ generic_def: None,
+ substs: AstSubsts::default(),
+ }
}
pub fn apply(&self, syntax: &SyntaxNode) {
@@ -91,12 +109,14 @@ impl<'a> PathTransform<'a> {
let target_module = self.target_scope.module();
let source_module = self.source_scope.module();
let skip = match self.generic_def {
- // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
+ // this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
Some(hir::GenericDef::Trait(_)) => 1,
_ => 0,
};
- let substs_by_param: FxHashMap<_, _> = self
- .generic_def
+ let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
+ let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
+ let mut default_types: Vec<hir::TypeParam> = Default::default();
+ self.generic_def
.into_iter()
.flat_map(|it| it.type_params(db))
.skip(skip)
@@ -106,51 +126,105 @@ impl<'a> PathTransform<'a> {
// can still hit those trailing values and check if they actually have
// a default type. If they do, go for that type from `hir` to `ast` so
// the resulting change can be applied correctly.
- .zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
- .filter_map(|(k, v)| match k.split(db) {
- Either::Left(_) => None,
- Either::Right(t) => match v {
- Some(v) => Some((k, v.clone())),
- None => {
- let default = t.default(db)?;
- Some((
- k,
- ast::make::ty(
- &default.display_source_code(db, source_module.into()).ok()?,
- ),
- ))
+ .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
+ .for_each(|(k, v)| match (k.split(db), v) {
+ (Either::Right(k), Some(TypeOrConst::Either(v))) => {
+ if let Some(ty) = v.ty() {
+ type_substs.insert(k, ty.clone());
+ }
+ }
+ (Either::Right(k), None) => {
+ if let Some(default) = k.default(db) {
+ if let Some(default) =
+ &default.display_source_code(db, source_module.into(), false).ok()
+ {
+ type_substs.insert(k, ast::make::ty(default).clone_for_update());
+ default_types.push(k);
+ }
+ }
+ }
+ (Either::Left(k), Some(TypeOrConst::Either(v))) => {
+ if let Some(ty) = v.ty() {
+ const_substs.insert(k, ty.syntax().clone());
}
- },
- })
+ }
+ (Either::Left(k), Some(TypeOrConst::Const(v))) => {
+ if let Some(expr) = v.expr() {
+ // FIXME: expressions in curly brackets can cause ambiguity after insertion
+ // (e.g. `N * 2` -> `{1 + 1} * 2`; it's unclear whether `{1 + 1}`
+ // is a standalone statement or a part of another expresson)
+ // and sometimes require slight modifications; see
+ // https://doc.rust-lang.org/reference/statements.html#expression-statements
+ const_substs.insert(k, expr.syntax().clone());
+ }
+ }
+ (Either::Left(_), None) => (), // FIXME: get default const value
+ _ => (), // ignore mismatching params
+ });
+ let lifetime_substs: FxHashMap<_, _> = self
+ .generic_def
+ .into_iter()
+ .flat_map(|it| it.lifetime_params(db))
+ .zip(self.substs.lifetimes.clone())
+ .filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
.collect();
- Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope }
+ let ctx = Ctx {
+ type_substs,
+ const_substs,
+ lifetime_substs,
+ target_module,
+ source_scope: self.source_scope,
+ };
+ ctx.transform_default_type_substs(default_types);
+ ctx
}
}
struct Ctx<'a> {
- substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
+ type_substs: FxHashMap<hir::TypeParam, ast::Type>,
+ const_substs: FxHashMap<hir::ConstParam, SyntaxNode>,
+ lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
target_module: hir::Module,
source_scope: &'a SemanticsScope<'a>,
}
+fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
+ item.preorder().filter_map(|event| match event {
+ syntax::WalkEvent::Enter(_) => None,
+ syntax::WalkEvent::Leave(node) => Some(node),
+ })
+}
+
impl<'a> Ctx<'a> {
fn apply(&self, item: &SyntaxNode) {
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
// so that such operation is safe.
- let paths = item
- .preorder()
- .filter_map(|event| match event {
- syntax::WalkEvent::Enter(_) => None,
- syntax::WalkEvent::Leave(node) => Some(node),
- })
- .filter_map(ast::Path::cast)
- .collect::<Vec<_>>();
-
+ let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
for path in paths {
self.transform_path(path);
}
+
+ postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
+ if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
+ ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
+ }
+ });
}
+
+ fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
+ for k in default_types {
+ let v = self.type_substs.get(&k).unwrap();
+ // `transform_path` may update a node's parent and that would break the
+ // tree traversal. Thus all paths in the tree are collected into a vec
+ // so that such operation is safe.
+ let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+ for path in paths {
+ self.transform_path(path);
+ }
+ }
+ }
+
fn transform_path(&self, path: ast::Path) -> Option<()> {
if path.qualifier().is_some() {
return None;
@@ -167,7 +241,7 @@ impl<'a> Ctx<'a> {
match resolution {
hir::PathResolution::TypeParam(tp) => {
- if let Some(subst) = self.substs.get(&tp.merge()) {
+ if let Some(subst) = self.type_substs.get(&tp) {
let parent = path.syntax().parent()?;
if let Some(parent) = ast::Path::cast(parent.clone()) {
// Path inside path means that there is an associated
@@ -234,8 +308,12 @@ impl<'a> Ctx<'a> {
}
ted::replace(path.syntax(), res.syntax())
}
+ hir::PathResolution::ConstParam(cp) => {
+ if let Some(subst) = self.const_substs.get(&cp) {
+ ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
+ }
+ }
hir::PathResolution::Local(_)
- | hir::PathResolution::ConstParam(_)
| hir::PathResolution::SelfType(_)
| hir::PathResolution::Def(_)
| hir::PathResolution::BuiltinAttr(_)
@@ -248,7 +326,7 @@ impl<'a> Ctx<'a> {
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
// trait ref, and then go from the types in the substs back to the syntax).
-fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
+fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {
let target_trait = impl_def.trait_()?;
let path_type = match target_trait {
ast::Type::PathType(path) => path,
@@ -259,13 +337,22 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
get_type_args_from_arg_list(generic_arg_list)
}
-fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> {
- let mut result = Vec::new();
- for generic_arg in generic_arg_list.generic_args() {
- if let ast::GenericArg::TypeArg(type_arg) = generic_arg {
- result.push(type_arg.ty()?)
+fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> {
+ let mut result = AstSubsts::default();
+ generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
+ // Const params are marked as consts on definition only,
+ // being passed to the trait they are indistguishable from type params;
+ // anyway, we don't really need to distinguish them here.
+ ast::GenericArg::TypeArg(type_arg) => {
+ result.types_and_consts.push(TypeOrConst::Either(type_arg))
}
- }
+ // Some const values are recognized correctly.
+ ast::GenericArg::ConstArg(const_arg) => {
+ result.types_and_consts.push(TypeOrConst::Const(const_arg));
+ }
+ ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
+ _ => (),
+ });
Some(result)
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index f710211c8..52a23b4b8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -178,7 +178,7 @@ fn rename_mod(
let mut source_change = SourceChange::default();
- if module.is_crate_root(sema.db) {
+ if module.is_crate_root() {
return Ok(source_change);
}
@@ -202,12 +202,13 @@ fn rename_mod(
// - Module has submodules defined in separate files
let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
// Go up one level since the anchor is inside the dir we're trying to rename
- (true, _, Some(mod_name)) => {
- Some((format!("../{}", mod_name.unescaped()), format!("../{new_name}")))
- }
+ (true, _, Some(mod_name)) => Some((
+ format!("../{}", mod_name.unescaped().display(sema.db)),
+ format!("../{new_name}"),
+ )),
// The anchor is on the same level as target dir
(false, true, Some(mod_name)) => {
- Some((mod_name.unescaped().to_string(), new_name.to_string()))
+ Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned()))
}
_ => None,
};
@@ -232,7 +233,7 @@ fn rename_mod(
{
source_change.insert_source_edit(
file_id,
- TextEdit::replace(file_range.range, new_name.to_string()),
+ TextEdit::replace(file_range.range, new_name.to_owned()),
)
};
}
@@ -442,7 +443,7 @@ fn source_edit_from_name_ref(
let s = field_name.syntax().text_range().start();
let e = pat.syntax().text_range().start();
edit.delete(TextRange::new(s, e));
- edit.replace(name.syntax().text_range(), new_name.to_string());
+ edit.replace(name.syntax().text_range(), new_name.to_owned());
return true;
}
}
@@ -462,7 +463,19 @@ fn source_edit_from_def(
if let Definition::Local(local) = def {
let mut file_id = None;
for source in local.sources(sema.db) {
- let source = source.source;
+ let source = match source.source.clone().original_ast_node(sema.db) {
+ Some(source) => source,
+ None => match source.source.syntax().original_file_range_opt(sema.db) {
+ Some(FileRange { file_id: file_id2, range }) => {
+ file_id = Some(file_id2);
+ edit.replace(range, new_name.to_owned());
+ continue;
+ }
+ None => {
+ bail!("Can't rename local that is defined in a macro declaration")
+ }
+ },
+ };
file_id = source.file_id.file_id();
if let Either::Left(pat) = source.value {
let name_range = pat.name().unwrap().syntax().text_range();
@@ -485,7 +498,7 @@ fn source_edit_from_def(
// Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
// Foo { field: ref mut local } -> Foo { field: ref mut new_name }
// ^^^^^ replace this with `new_name`
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
} else {
// Foo { ref mut field } -> Foo { field: ref mut new_name }
@@ -495,10 +508,10 @@ fn source_edit_from_def(
pat.syntax().text_range().start(),
format!("{}: ", pat_field.field_name().unwrap()),
);
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
} else {
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 12f5e4e2a..e8ff107bd 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -4,17 +4,18 @@
//! get a super-set of matches. Then, we we confirm each match using precise
//! name resolution.
-use std::{mem, sync::Arc};
+use std::mem;
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
};
use memchr::memmem::Finder;
+use nohash_hasher::IntMap;
use once_cell::unsync::Lazy;
use parser::SyntaxKind;
-use stdx::hash::NoHashHashMap;
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
+use triomphe::Arc;
use crate::{
defs::{Definition, NameClass, NameRefClass},
@@ -24,7 +25,7 @@ use crate::{
#[derive(Debug, Default, Clone)]
pub struct UsageSearchResult {
- pub references: NoHashHashMap<FileId, Vec<FileReference>>,
+ pub references: IntMap<FileId, Vec<FileReference>>,
}
impl UsageSearchResult {
@@ -49,7 +50,7 @@ impl UsageSearchResult {
impl IntoIterator for UsageSearchResult {
type Item = (FileId, Vec<FileReference>);
- type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
+ type IntoIter = <IntMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.references.into_iter()
@@ -83,17 +84,17 @@ pub enum ReferenceCategory {
/// e.g. for things like local variables.
#[derive(Clone, Debug)]
pub struct SearchScope {
- entries: NoHashHashMap<FileId, Option<TextRange>>,
+ entries: IntMap<FileId, Option<TextRange>>,
}
impl SearchScope {
- fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope {
+ fn new(entries: IntMap<FileId, Option<TextRange>>) -> SearchScope {
SearchScope { entries }
}
/// Build a search scope spanning the entire crate graph of files.
fn crate_graph(db: &RootDatabase) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
let graph = db.crate_graph();
for krate in graph.iter() {
@@ -107,7 +108,7 @@ impl SearchScope {
/// Build a search scope spanning all the reverse dependencies of the given crate.
fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
for rev_dep in of.transitive_reverse_dependencies(db) {
let root_file = rev_dep.root_file(db);
let source_root_id = db.file_source_root(root_file);
@@ -127,7 +128,7 @@ impl SearchScope {
/// Build a search scope spanning the given module and all its submodules.
fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
let (file_id, range) = {
let InFile { file_id, value } = module.definition_source(db);
@@ -160,7 +161,7 @@ impl SearchScope {
/// Build an empty search scope.
pub fn empty() -> SearchScope {
- SearchScope::new(NoHashHashMap::default())
+ SearchScope::new(IntMap::default())
}
/// Build a empty search scope spanning the given file.
@@ -224,7 +225,7 @@ impl Definition {
// def is crate root
// FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
- if module.is_crate_root(db) {
+ if module.is_crate_root() {
return SearchScope::reverse_dependencies(db, module.krate());
}
}
@@ -242,6 +243,8 @@ impl Definition {
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
+ // FIXME: implement
+ DefWithBody::InTypeConst(_) => return SearchScope::empty(),
};
return match def {
Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
@@ -391,7 +394,7 @@ impl<'a> FindUsages<'a> {
let name = match self.def {
// special case crate modules as these do not have a proper name
- Definition::Module(module) if module.is_crate_root(self.sema.db) => {
+ Definition::Module(module) if module.is_crate_root() => {
// FIXME: This assumes the crate name is always equal to its display name when it really isn't
module
.krate()
@@ -438,11 +441,11 @@ impl<'a> FindUsages<'a> {
fn scope_files<'a>(
sema: &'a Semantics<'_, RootDatabase>,
scope: &'a SearchScope,
- ) -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a {
+ ) -> impl Iterator<Item = (Arc<str>, FileId, TextRange)> + 'a {
scope.entries.iter().map(|(&file_id, &search_range)| {
let text = sema.db.file_text(file_id);
let search_range =
- search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
+ search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
(text, file_id, search_range)
})
@@ -499,7 +502,7 @@ impl<'a> FindUsages<'a> {
let scope =
search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module));
- let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
+ let is_crate_root = module.is_crate_root().then(|| Finder::new("crate"));
let finder = &Finder::new("super");
for (text, file_id, search_range) in scope_files(sema, &scope) {
@@ -553,7 +556,7 @@ impl<'a> FindUsages<'a> {
let text = sema.db.file_text(file_id);
let search_range =
- search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
+ search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
let finder = &Finder::new("self");
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 936354f29..061fb0f05 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -5,16 +5,16 @@
use std::{collections::hash_map::Entry, iter, mem};
+use crate::SnippetCap;
use base_db::{AnchoredPathBuf, FileId};
-use stdx::{hash::NoHashHashMap, never};
-use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
+use nohash_hasher::IntMap;
+use stdx::never;
+use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
use text_edit::{TextEdit, TextEditBuilder};
-use crate::SnippetCap;
-
#[derive(Default, Debug, Clone)]
pub struct SourceChange {
- pub source_file_edits: NoHashHashMap<FileId, TextEdit>,
+ pub source_file_edits: IntMap<FileId, TextEdit>,
pub file_system_edits: Vec<FileSystemEdit>,
pub is_snippet: bool,
}
@@ -23,7 +23,7 @@ impl SourceChange {
/// Creates a new SourceChange with the given label
/// from the edits.
pub fn from_edits(
- source_file_edits: NoHashHashMap<FileId, TextEdit>,
+ source_file_edits: IntMap<FileId, TextEdit>,
file_system_edits: Vec<FileSystemEdit>,
) -> Self {
SourceChange { source_file_edits, file_system_edits, is_snippet: false }
@@ -77,8 +77,8 @@ impl Extend<FileSystemEdit> for SourceChange {
}
}
-impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
- fn from(source_file_edits: NoHashHashMap<FileId, TextEdit>) -> SourceChange {
+impl From<IntMap<FileId, TextEdit>> for SourceChange {
+ fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange {
SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
}
}
@@ -99,6 +99,8 @@ pub struct SourceChangeBuilder {
/// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
pub mutated_tree: Option<TreeMutator>,
+ /// Keeps track of where to place snippets
+ pub snippet_builder: Option<SnippetBuilder>,
}
pub struct TreeMutator {
@@ -106,6 +108,12 @@ pub struct TreeMutator {
mutable_clone: SyntaxNode,
}
+#[derive(Default)]
+pub struct SnippetBuilder {
+ /// Where to place snippets at
+ places: Vec<PlaceSnippet>,
+}
+
impl TreeMutator {
pub fn new(immutable: &SyntaxNode) -> TreeMutator {
let immutable = immutable.ancestors().last().unwrap();
@@ -131,6 +139,7 @@ impl SourceChangeBuilder {
source_change: SourceChange::default(),
trigger_signature_help: false,
mutated_tree: None,
+ snippet_builder: None,
}
}
@@ -140,6 +149,17 @@ impl SourceChangeBuilder {
}
fn commit(&mut self) {
+ // Render snippets first so that they get bundled into the tree diff
+ if let Some(mut snippets) = self.snippet_builder.take() {
+ // Last snippet always has stop index 0
+ let last_stop = snippets.places.pop().unwrap();
+ last_stop.place(0);
+
+ for (index, stop) in snippets.places.into_iter().enumerate() {
+ stop.place(index + 1)
+ }
+ }
+
if let Some(tm) = self.mutated_tree.take() {
algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
}
@@ -161,7 +181,7 @@ impl SourceChangeBuilder {
/// mutability, and different nodes in the same tree see the same mutations.
///
/// The typical pattern for an assist is to find specific nodes in the read
- /// phase, and then get their mutable couterparts using `make_mut` in the
+ /// phase, and then get their mutable counterparts using `make_mut` in the
/// mutable state.
pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
@@ -214,6 +234,30 @@ impl SourceChangeBuilder {
self.trigger_signature_help = true;
}
+ /// Adds a tabstop snippet to place the cursor before `node`
+ pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::Before(node.syntax().clone()));
+ }
+
+ /// Adds a tabstop snippet to place the cursor after `node`
+ pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::After(node.syntax().clone()));
+ }
+
+ /// Adds a snippet to move the cursor selected over `node`
+ pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::Over(node.syntax().clone()))
+ }
+
+ fn add_snippet(&mut self, snippet: PlaceSnippet) {
+ let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] });
+ snippet_builder.places.push(snippet);
+ self.source_change.is_snippet = true;
+ }
+
pub fn finish(mut self) -> SourceChange {
self.commit();
mem::take(&mut self.source_change)
@@ -236,3 +280,66 @@ impl From<FileSystemEdit> for SourceChange {
}
}
}
+
+enum PlaceSnippet {
+ /// Place a tabstop before a node
+ Before(SyntaxNode),
+ /// Place a tabstop before a node
+ After(SyntaxNode),
+ /// Place a placeholder snippet in place of the node
+ Over(SyntaxNode),
+}
+
+impl PlaceSnippet {
+ /// Places the snippet before or over a node with the given tab stop index
+ fn place(self, order: usize) {
+ // ensure the target node is still attached
+ match &self {
+ PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => {
+ // node should still be in the tree, but if it isn't
+ // then it's okay to just ignore this place
+ if stdx::never!(node.parent().is_none()) {
+ return;
+ }
+ }
+ }
+
+ match self {
+ PlaceSnippet::Before(node) => {
+ ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order));
+ }
+ PlaceSnippet::After(node) => {
+ ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order));
+ }
+ PlaceSnippet::Over(node) => {
+ let position = ted::Position::before(&node);
+ node.detach();
+
+ let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}"))
+ .syntax_node()
+ .clone_for_update();
+
+ let placeholder =
+ snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap();
+ ted::replace(placeholder.syntax(), node);
+
+ ted::insert_raw(position, snippet);
+ }
+ }
+ }
+
+ fn make_tab_stop(order: usize) -> SyntaxNode {
+ let stop = ast::SourceFile::parse(&format!("stop!(${order})"))
+ .syntax_node()
+ .descendants()
+ .find_map(ast::TokenTree::cast)
+ .unwrap()
+ .syntax()
+ .clone_for_update();
+
+ stop.first_token().unwrap().detach();
+ stop.last_token().unwrap().detach();
+
+ stop
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index a91ffd1ec..b54c43b29 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -25,7 +25,6 @@ use std::{
fmt,
hash::{Hash, Hasher},
mem,
- sync::Arc,
};
use base_db::{
@@ -40,6 +39,7 @@ use hir::{
};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use crate::RootDatabase;
@@ -98,6 +98,10 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab
/// The symbol index for a given source root within library_roots.
fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>;
+ #[salsa::transparent]
+ /// The symbol indices of modules that make up a given crate.
+ fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>;
+
/// The set of "local" (that is, from the current workspace) roots.
/// Files in local roots are assumed to change frequently.
#[salsa::input]
@@ -112,26 +116,33 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab
fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc<SymbolIndex> {
let _p = profile::span("library_symbols");
- // todo: this could be parallelized, once I figure out how to do that...
- let symbols = db
- .source_root_crates(source_root_id)
+ let mut symbol_collector = SymbolCollector::new(db.upcast());
+
+ db.source_root_crates(source_root_id)
.iter()
.flat_map(|&krate| Crate::from(krate).modules(db.upcast()))
- // we specifically avoid calling SymbolsDatabase::module_symbols here, even they do the same thing,
+ // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing,
// as the index for a library is not going to really ever change, and we do not want to store each
- // module's index in salsa.
- .flat_map(|module| SymbolCollector::collect(db.upcast(), module))
- .collect();
+ // the module or crate indices for those in salsa unless we need to.
+ .for_each(|module| symbol_collector.collect(module));
+ let mut symbols = symbol_collector.finish();
+ symbols.shrink_to_fit();
Arc::new(SymbolIndex::new(symbols))
}
fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
let _p = profile::span("module_symbols");
- let symbols = SymbolCollector::collect(db.upcast(), module);
+
+ let symbols = SymbolCollector::collect_module(db.upcast(), module);
Arc::new(SymbolIndex::new(symbols))
}
+pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
+ let _p = profile::span("crate_symbols");
+ krate.modules(db.upcast()).into_iter().map(|module| db.module_symbols(module)).collect()
+}
+
/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap<DB>(DB);
impl<DB: ParallelDatabase> Snap<salsa::Snapshot<DB>> {
@@ -187,36 +198,21 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
.map_with(Snap::new(db), |snap, &root| snap.library_symbols(root))
.collect()
} else {
- let mut modules = Vec::new();
+ let mut crates = Vec::new();
for &root in db.local_roots().iter() {
- let crates = db.source_root_crates(root);
- for &krate in crates.iter() {
- modules.extend(Crate::from(krate).modules(db));
- }
+ crates.extend(db.source_root_crates(root).iter().copied())
}
-
- modules
- .par_iter()
- .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module))
- .collect()
+ let indices: Vec<_> = crates
+ .into_par_iter()
+ .map_with(Snap::new(db), |snap, krate| snap.crate_symbols(krate.into()))
+ .collect();
+ indices.iter().flat_map(|indices| indices.iter().cloned()).collect()
};
query.search(&indices)
}
-pub fn crate_symbols(db: &RootDatabase, krate: Crate, query: Query) -> Vec<FileSymbol> {
- let _p = profile::span("crate_symbols").detail(|| format!("{query:?}"));
-
- let modules = krate.modules(db);
- let indices: Vec<_> = modules
- .par_iter()
- .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module))
- .collect();
-
- query.search(&indices)
-}
-
#[derive(Default)]
pub struct SymbolIndex {
symbols: Vec<FileSymbol>,
@@ -274,7 +270,12 @@ impl SymbolIndex {
builder.insert(key, value).unwrap();
}
- let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
+ let map = fst::Map::new({
+ let mut buf = builder.into_inner().unwrap();
+ buf.shrink_to_fit();
+ buf
+ })
+ .unwrap();
SymbolIndex { symbols, map }
}
@@ -316,7 +317,14 @@ impl Query {
let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
for symbol in &symbol_index.symbols[start..end] {
- if self.only_types && !symbol.kind.is_type() {
+ if self.only_types
+ && !matches!(
+ symbol.def,
+ hir::ModuleDef::Adt(..)
+ | hir::ModuleDef::TypeAlias(..)
+ | hir::ModuleDef::BuiltinType(..)
+ )
+ {
continue;
}
if self.exact {
@@ -418,7 +426,7 @@ struct StructInModB;
.modules(&db)
.into_iter()
.map(|module_id| {
- let mut symbols = SymbolCollector::collect(&db, module_id);
+ let mut symbols = SymbolCollector::collect_module(&db, module_id);
symbols.sort_by_key(|it| it.name.clone());
(module_id, symbols)
})
@@ -426,4 +434,31 @@ struct StructInModB;
expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols);
}
+
+ #[test]
+ fn test_doc_alias() {
+ let (db, _) = RootDatabase::with_single_file(
+ r#"
+#[doc(alias="s1")]
+#[doc(alias="s2")]
+#[doc(alias("mul1","mul2"))]
+struct Struct;
+
+#[doc(alias="s1")]
+struct Duplicate;
+ "#,
+ );
+
+ let symbols: Vec<_> = Crate::from(db.test_crate())
+ .modules(&db)
+ .into_iter()
+ .map(|module_id| {
+ let mut symbols = SymbolCollector::collect_module(&db, module_id);
+ symbols.sort_by_key(|it| it.name.clone());
+ (module_id, symbols)
+ })
+ .collect();
+
+ expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols);
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
index 2d6927cee..acf0a67de 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -92,7 +92,7 @@ pub fn lex_format_specifiers(
let (_, second) = cloned.next().unwrap_or_default();
match second {
'<' | '^' | '>' => {
- // alignment specifier, first char specifies fillment
+ // alignment specifier, first char specifies fill
skip_char_and_emit(&mut chars, FormatSpecifier::Fill, &mut callback);
skip_char_and_emit(&mut chars, FormatSpecifier::Align, &mut callback);
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index fcef71fb7..fc2308181 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -1,7 +1,7 @@
//! Tools to work with expressions present in format string literals for the `format_args!` family of macros.
//! Primarily meant for assists and completions.
-/// Enum for represenging extraced format string args.
+/// Enum for representing extracted format string args.
/// Can either be extracted expressions (which includes identifiers),
/// or placeholders `{}`.
#[derive(Debug, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
index 8bc093a85..0b0fc6693 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
@@ -60,7 +60,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
|f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
match tok.kind() {
- k if is_text(k) && is_next(|it| !it.is_punct() || it == UNDERSCORE, false) => {
+ k if is_text(k)
+ && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) =>
+ {
mods.push(do_ws(after, tok));
}
L_CURLY if is_next(|it| it != R_CURLY, true) => {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index a34dc1b69..22ced69d8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -52,7 +52,9 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>)
}
};
if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) {
- if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) {
+ if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true)
+ && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true)
+ {
// skipping potential const pat expressions in let statements
preorder.skip_subtree();
continue;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
new file mode 100644
index 000000000..7834c6603
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -0,0 +1,216 @@
+[
+ (
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(0),
+ },
+ },
+ [
+ FileSymbol {
+ name: "Duplicate",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 83..119,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 109..118,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "Struct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "mul1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "mul2",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 83..119,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 109..118,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s2",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ ],
+ ),
+]
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 8c11408de..1a00e2938 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,9 +2,7 @@
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(0),
},
@@ -12,9 +10,18 @@
[
FileSymbol {
name: "Alias",
+ def: TypeAlias(
+ TypeAlias {
+ id: TypeAliasId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: TYPE_ALIAS,
@@ -25,14 +32,23 @@
range: 402..407,
},
},
- kind: TypeAlias,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "CONST",
+ def: Const(
+ Const {
+ id: ConstId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: CONST,
@@ -43,14 +59,23 @@
range: 346..351,
},
},
- kind: Const,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "CONST_WITH_INNER",
+ def: Const(
+ Const {
+ id: ConstId(
+ 2,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: CONST,
@@ -61,14 +86,25 @@
range: 526..542,
},
},
- kind: Const,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Enum",
+ def: Adt(
+ Enum(
+ Enum {
+ id: EnumId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: ENUM,
@@ -79,14 +115,25 @@
range: 190..194,
},
},
- kind: Enum,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Macro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_DEF,
@@ -97,14 +144,23 @@
range: 159..164,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "STATIC",
+ def: Static(
+ Static {
+ id: StaticId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STATIC,
@@ -115,14 +171,25 @@
range: 369..375,
},
},
- kind: Static,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Struct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -133,14 +200,27 @@
range: 177..183,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "StructFromMacro",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 2147483648,
+ hir_file_id: MacroFile(
+ MacroFile {
+ macro_call_id: MacroCallId(
+ 0,
+ ),
+ },
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -151,14 +231,25 @@
range: 6..21,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "StructInFn",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 4,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -169,16 +260,27 @@
range: 325..335,
},
},
- kind: Struct,
container_name: Some(
"main",
),
+ is_alias: false,
},
FileSymbol {
name: "StructInNamedConst",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 5,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -189,16 +291,27 @@
range: 562..580,
},
},
- kind: Struct,
container_name: Some(
"CONST_WITH_INNER",
),
+ is_alias: false,
},
FileSymbol {
name: "StructInUnnamedConst",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 6,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -209,14 +322,23 @@
range: 486..506,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Trait",
+ def: Trait(
+ Trait {
+ id: TraitId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: TRAIT,
@@ -227,14 +349,25 @@
range: 267..272,
},
},
- kind: Trait,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Union",
+ def: Adt(
+ Union(
+ Union {
+ id: UnionId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: UNION,
@@ -245,14 +378,25 @@
range: 214..219,
},
},
- kind: Union,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "a_mod",
+ def: Module(
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(1),
+ },
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MODULE,
@@ -263,14 +407,25 @@
range: 423..428,
},
},
- kind: Module,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "b_mod",
+ def: Module(
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(2),
+ },
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MODULE,
@@ -281,14 +436,25 @@
range: 598..603,
},
},
- kind: Module,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "define_struct",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 1,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_RULES,
@@ -299,14 +465,23 @@
range: 64..77,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "impl_fn",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 2,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -317,14 +492,25 @@
range: 245..252,
},
},
- kind: Function,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "macro_rules_macro",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 0,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_RULES,
@@ -335,14 +521,23 @@
range: 14..31,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "main",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -353,14 +548,23 @@
range: 305..309,
},
},
- kind: Function,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "trait_fn",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 1,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -371,19 +575,17 @@
range: 282..290,
},
},
- kind: Function,
container_name: Some(
"Trait",
),
+ is_alias: false,
},
],
),
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(1),
},
@@ -391,9 +593,20 @@
[
FileSymbol {
name: "StructInModA",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 2,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -404,17 +617,15 @@
range: 442..454,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
],
),
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(2),
},
@@ -422,9 +633,20 @@
[
FileSymbol {
name: "StructInModB",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 1,
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -435,8 +657,8 @@
range: 7..19,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
],
),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs
new file mode 100644
index 000000000..6b49bb263
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs
@@ -0,0 +1,49 @@
+use line_index::{LineCol, LineIndex, WideEncoding};
+use test_utils::skip_slow_tests;
+
+#[test]
+fn test_every_chars() {
+ if skip_slow_tests() {
+ return;
+ }
+
+ let text: String = {
+ let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat!
+ chars.extend("\n".repeat(chars.len() / 16).chars());
+ let mut rng = oorandom::Rand32::new(stdx::rand::seed());
+ stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize);
+ chars.into_iter().collect()
+ };
+ assert!(text.contains('💩')); // Sanity check.
+
+ let line_index = LineIndex::new(&text);
+
+ let mut lin_col = LineCol { line: 0, col: 0 };
+ let mut col_utf16 = 0;
+ let mut col_utf32 = 0;
+ for (offset, c) in text.char_indices() {
+ let got_offset = line_index.offset(lin_col).unwrap();
+ assert_eq!(usize::from(got_offset), offset);
+
+ let got_lin_col = line_index.line_col(got_offset);
+ assert_eq!(got_lin_col, lin_col);
+
+ for (enc, col) in [(WideEncoding::Utf16, col_utf16), (WideEncoding::Utf32, col_utf32)] {
+ let wide_lin_col = line_index.to_wide(enc, lin_col).unwrap();
+ let got_lin_col = line_index.to_utf8(enc, wide_lin_col).unwrap();
+ assert_eq!(got_lin_col, lin_col);
+ assert_eq!(wide_lin_col.col, col)
+ }
+
+ if c == '\n' {
+ lin_col.line += 1;
+ lin_col.col = 0;
+ col_utf16 = 0;
+ col_utf32 = 0;
+ } else {
+ lin_col.col += c.len_utf8() as u32;
+ col_utf16 += c.len_utf16() as u32;
+ col_utf32 += 1;
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
index 6a7ea7c19..9abbc3441 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
@@ -38,15 +38,15 @@ pub fn get_missing_assoc_items(
for item in imp.items(sema.db) {
match item {
hir::AssocItem::Function(it) => {
- impl_fns_consts.insert(it.name(sema.db).to_string());
+ impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string());
}
hir::AssocItem::Const(it) => {
if let Some(name) = it.name(sema.db) {
- impl_fns_consts.insert(name.to_string());
+ impl_fns_consts.insert(name.display(sema.db).to_string());
}
}
hir::AssocItem::TypeAlias(it) => {
- impl_type.insert(it.name(sema.db).to_string());
+ impl_type.insert(it.name(sema.db).display(sema.db).to_string());
}
}
}
@@ -57,12 +57,14 @@ pub fn get_missing_assoc_items(
.into_iter()
.filter(|i| match i {
hir::AssocItem::Function(f) => {
- !impl_fns_consts.contains(&f.name(sema.db).to_string())
+ !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string())
+ }
+ hir::AssocItem::TypeAlias(t) => {
+ !impl_type.contains(&t.name(sema.db).display(sema.db).to_string())
}
- hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
hir::AssocItem::Const(c) => c
.name(sema.db)
- .map(|n| !impl_fns_consts.contains(&n.to_string()))
+ .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string()))
.unwrap_or_default(),
})
.collect()
@@ -137,7 +139,7 @@ mod tests {
sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
let actual = match trait_ {
- Some(trait_) => trait_.name(&db).to_string(),
+ Some(trait_) => trait_.name(&db).display(&db).to_string(),
None => String::new(),
};
expect.assert_eq(&actual);
@@ -152,7 +154,7 @@ mod tests {
let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
let actual = items
.into_iter()
- .map(|item| item.name(&db).unwrap().to_string())
+ .map(|item| item.name(&db).unwrap().display(&db).to_string())
.collect::<Vec<_>>()
.join("\n");
expect.assert_eq(&actual);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index 39431bed3..f96ea29ae 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,9 +1,9 @@
-//! Functionality for generating trivial contructors
+//! Functionality for generating trivial constructors
use hir::StructKind;
use syntax::ast;
-/// given a type return the trivial contructor (if one exists)
+/// given a type return the trivial constructor (if one exists)
pub fn use_trivial_constructor(
db: &crate::RootDatabase,
path: ast::Path,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 114face2d..30576c71f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -31,12 +31,8 @@ mod tests {
fn foo() {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
}
"#,
);
@@ -51,12 +47,8 @@ fn foo() {
async {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
};
}
}
@@ -73,12 +65,8 @@ fn foo() {
|| {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
};
}
}
@@ -94,9 +82,7 @@ fn foo() {
'a: loop {
{
break;
- break 'a;
continue;
- continue 'a;
}
}
}
@@ -112,9 +98,7 @@ fn foo() {
'a: loop {
try {
break;
- break 'a;
continue;
- continue 'a;
};
}
}
@@ -130,11 +114,8 @@ fn foo() {
'a: {
break;
//^^^^^ error: break outside of loop
- break 'a;
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
}
}
"#,
@@ -143,15 +124,35 @@ fn foo() {
#[test]
fn value_break_in_for_loop() {
+ // FIXME: the error is correct, but the message is terrible
check_diagnostics(
r#"
+//- minicore: iterator
fn test() {
for _ in [()] {
break 3;
- // ^^^^^^^ error: can't break with a value in this position
+ // ^ error: expected (), found i32
}
}
"#,
);
}
+
+ #[test]
+ fn try_block_desugaring_inside_closure() {
+ // regression test for #14701
+ check_diagnostics(
+ r#"
+//- minicore: option, try
+fn test() {
+ try {
+ || {
+ let x = Some(2);
+ Some(x?)
+ };
+ };
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index db88bf7b9..90279e145 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -27,7 +27,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let name_node = d.ident.to_node(&root);
let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?;
@@ -295,7 +295,7 @@ impl someStruct {
}
#[test]
- fn no_diagnostic_for_enum_varinats() {
+ fn no_diagnostic_for_enum_variants() {
check_diagnostics(
r#"
enum Option { Some, None }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 870c78d1f..7547779a9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -9,6 +9,16 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
}
+// Diagnostic: macro-error
+//
+// This diagnostic is shown for macro expansion errors.
+pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic {
+ // Use more accurate position if available.
+ let display_range =
+ ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name);
+ Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental()
+}
+
#[cfg(test)]
mod tests {
use crate::{
@@ -188,6 +198,7 @@ fn f() {
"#,
);
}
+
#[test]
fn dollar_crate_in_builtin_macro() {
check_diagnostics(
@@ -212,4 +223,38 @@ fn f() {
"#,
)
}
+
+ #[test]
+ fn def_diagnostic() {
+ check_diagnostics(
+ r#"
+macro_rules! foo {
+ //^^^ error: expected subtree
+ f => {};
+}
+
+fn f() {
+ foo!();
+ //^^^ error: invalid macro definition: expected subtree
+
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn expansion_syntax_diagnostic() {
+ check_diagnostics(
+ r#"
+macro_rules! foo {
+ () => { struct; };
+}
+
+fn f() {
+ foo!();
+ //^^^ error: Syntax Error in Expansion: expected a name
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 5c4327ff9..60ccc41df 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -31,7 +31,7 @@ use crate::{fix, Diagnostic, DiagnosticsContext};
pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
let mut message = String::from("missing structure fields:\n");
for field in &d.missed_fields {
- format_to!(message, "- {}\n", field);
+ format_to!(message, "- {}\n", field.display(ctx.sema.db));
}
let ptr = InFile::new(
@@ -56,7 +56,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
return None;
}
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let current_module = match &d.field_list_parent {
Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
@@ -175,8 +175,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
let ty_str = match ty.as_adt() {
- Some(adt) => adt.name(db).to_string(),
- None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()),
+ Some(adt) => adt.name(db).display(db.upcast()).to_string(),
+ None => {
+ ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string())
+ }
};
make::ty(&ty_str)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index ac4463331..3f13b97a4 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -271,15 +271,20 @@ enum Either2 { C, D }
fn main() {
match Either::A {
Either2::C => (),
+ //^^^^^^^^^^ error: expected Either, found Either2
Either2::D => (),
+ //^^^^^^^^^^ error: expected Either, found Either2
}
match (true, false) {
(true, false, true) => (),
+ //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool)
(true) => (),
// ^^^^ error: expected (bool, bool), found bool
}
match (true, false) { (true,) => {} }
+ //^^^^^^^ error: expected (bool, bool), found (bool,)
match (0) { () => () }
+ //^^ error: expected i32, found ()
match Unresolved::Bar { Unresolved::Baz => () }
}
"#,
@@ -293,7 +298,9 @@ fn main() {
r#"
fn main() {
match false { true | () => {} }
+ //^^ error: expected bool, found ()
match (false,) { (true | (),) => {} }
+ //^^ error: expected bool, found ()
}
"#,
);
@@ -738,17 +745,13 @@ fn main() {
#[test]
fn binding_ref_has_correct_type() {
- cov_mark::check_count!(validate_match_bailed_out, 1);
-
// Asserts `PatKind::Binding(ref _x): bool`, not &bool.
// If that's not true match checking will panic with "incompatible constructors"
// FIXME: make facilities to test this directly like `tests::check_infer(..)`
- check_diagnostics(
+ check_diagnostics_no_bails(
r#"
enum Foo { A }
fn main() {
- // FIXME: this should not bail out but current behavior is such as the old algorithm.
- // ExprValidator::validate_match(..) checks types of top level patterns incorrectly.
match Foo::A {
ref _x => {}
Foo::A => {}
@@ -1024,6 +1027,7 @@ fn main() {
check_diagnostics(
r#"
+//- minicore: copy
fn main() {
match &false {
&true => {}
@@ -1035,11 +1039,13 @@ fn main() {
#[test]
fn reference_patterns_in_fields() {
- cov_mark::check_count!(validate_match_bailed_out, 2);
+ cov_mark::check_count!(validate_match_bailed_out, 1);
check_diagnostics(
r#"
+//- minicore: copy
fn main() {
match (&false,) {
+ //^^^^^^^^^ error: missing match arm: `(&false,)` not covered
(true,) => {}
}
match (&false,) {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index eb32db250..2026b6fce 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -24,7 +24,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
return None;
}
- let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
let expr = d.expr.value.to_node(&root);
let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
@@ -142,6 +142,8 @@ fn main() {
fn missing_unsafe_diagnostic_with_static_mut() {
check_diagnostics(
r#"
+//- minicore: copy
+
struct Ty {
a: u8,
}
@@ -256,6 +258,7 @@ fn main() {
fn add_unsafe_block_when_accessing_mutable_static() {
check_fix(
r#"
+//- minicore: copy
struct Ty {
a: u8,
}
@@ -374,6 +377,7 @@ fn main() {
fn unsafe_expr_as_right_hand_side_of_assignment() {
check_fix(
r#"
+//- minicore: copy
static mut STATIC_MUT: u8 = 0;
fn main() {
@@ -396,6 +400,7 @@ fn main() {
fn unsafe_expr_in_binary_plus() {
check_fix(
r#"
+//- minicore: copy
static mut STATIC_MUT: u8 = 0;
fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
new file mode 100644
index 000000000..32e321107
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -0,0 +1,175 @@
+use crate::{Diagnostic, DiagnosticsContext};
+use hir::HirDisplay;
+
+// Diagnostic: moved-out-of-ref
+//
+// This diagnostic is triggered on moving non copy things out of references.
+pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic {
+ Diagnostic::new(
+ "moved-out-of-ref",
+ format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.span.clone()).range,
+ )
+ .experimental() // spans are broken, and I'm not sure how precise we can detect copy types
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ // FIXME: spans are broken
+
+ #[test]
+ fn move_by_explicit_deref() {
+ check_diagnostics(
+ r#"
+struct X;
+fn main() {
+ let a = &X;
+ let b = *a;
+ //^ error: cannot move `X` out of reference
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_out_of_field() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+struct X;
+struct Y(X, i32);
+fn main() {
+ let a = &Y(X, 5);
+ let b = a.0;
+ //^ error: cannot move `X` out of reference
+ let y = a.1;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_out_of_static() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+struct X;
+fn main() {
+ static S: X = X;
+ let s = S;
+ //^ error: cannot move `X` out of reference
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn generic_types() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, copy
+
+#[derive(Copy)]
+struct X<T>(T);
+struct Y;
+
+fn consume<T>(_: X<T>) {
+
+}
+
+fn main() {
+ let a = &X(Y);
+ consume(*a);
+ //^^^^^^^^^^^ error: cannot move `X<Y>` out of reference
+ let a = &X(5);
+ consume(*a);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_simple() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+fn f(_: i32) {}
+fn main() {
+ let x = &2;
+ f(*x);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_unknown_type() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, copy
+fn f(x: &Unknown) -> Unknown {
+ *x
+}
+
+#[derive(Copy)]
+struct X<T>(T);
+
+struct Y<T>(T);
+
+fn g(x: &X<Unknown>) -> X<Unknown> {
+ *x
+}
+
+fn h(x: &Y<Unknown>) -> Y<Unknown> {
+ // FIXME: we should show error for this, as `Y` is not copy
+ // regardless of its generic parameter.
+ *x
+}
+
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_dyn_fn() {
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f(x: &mut &mut dyn Fn()) {
+ x();
+}
+
+struct X<'a> {
+ field: &'a mut dyn Fn(),
+}
+
+fn f(x: &mut X<'_>) {
+ (x.field)();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_match_and_closure_capture() {
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+enum X {
+ Foo(u16),
+ Bar,
+}
+
+fn main() {
+ let x = &X::Bar;
+ let c = || match *x {
+ X::Foo(t) => t,
+ _ => 5,
+ };
+}
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 96470265d..f61460e31 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -18,7 +18,8 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
let use_range = d.span.value.text_range();
for source in d.local.sources(ctx.sema.db) {
let Some(ast) = source.name() else { continue };
- edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
+ // FIXME: macros
+ edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_string());
}
let edit = edit_builder.finish();
Some(vec![fix(
@@ -30,7 +31,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
})();
Diagnostic::new(
"need-mut",
- format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
+ format!(
+ "cannot mutate immutable variable `{}`",
+ d.local.name(ctx.sema.db).display(ctx.sema.db)
+ ),
ctx.sema.diagnostics_display_range(d.span.clone()).range,
)
.with_fixes(fixes)
@@ -340,6 +344,7 @@ fn main() {
fn regression_14310() {
check_diagnostics(
r#"
+ //- minicore: copy, builtin_impls
fn clone(mut i: &!) -> ! {
//^^^^^ 💡 weak: variable does not need to be mutable
*i
@@ -349,6 +354,32 @@ fn main() {
}
#[test]
+ fn match_closure_capture() {
+ check_diagnostics(
+ r#"
+//- minicore: option
+fn main() {
+ let mut v = &mut Some(2);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let _ = || match v {
+ Some(k) => {
+ *k = 5;
+ }
+ None => {}
+ };
+ let v = &mut Some(2);
+ let _ = || match v {
+ //^ 💡 error: cannot mutate immutable variable `v`
+ ref mut k => {
+ *k = &mut Some(5);
+ }
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
fn match_bindings() {
check_diagnostics(
r#"
@@ -368,7 +399,7 @@ fn main() {
#[test]
fn mutation_in_dead_code() {
// This one is interesting. Dead code is not represented at all in the MIR, so
- // there would be no mutablility error for locals in dead code. Rustc tries to
+ // there would be no mutability error for locals in dead code. Rustc tries to
// not emit `unused_mut` in this case, but since it works without `mut`, and
// special casing it is not trivial, we emit it.
check_diagnostics(
@@ -485,6 +516,38 @@ fn main() {
);
check_diagnostics(
r#"
+fn check(_: i32) -> bool {
+ false
+}
+fn main() {
+ loop {
+ let x = 1;
+ if check(x) {
+ break;
+ }
+ let y = (1, 2);
+ if check(y.1) {
+ return;
+ }
+ let z = (1, 2);
+ match z {
+ (k @ 5, ref mut t) if { continue; } => {
+ //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z`
+ *t = 5;
+ }
+ _ => {
+ let y = (1, 2);
+ if check(y.1) {
+ return;
+ }
+ }
+ }
+ }
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
fn f(_: i32) {}
fn main() {
loop {
@@ -546,13 +609,35 @@ fn f(x: i32) {
}
"#,
);
+ check_diagnostics(
+ r#"
+fn f((x, y): (i32, i32)) {
+ let t = [0; 2];
+ x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_diagnostics_in_case_of_multiple_bounds() {
+ check_diagnostics(
+ r#"
+fn f() {
+ let (b, a, b) = (2, 3, 5);
+ a = 8;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+}
+"#,
+ );
}
#[test]
fn for_loop() {
check_diagnostics(
r#"
-//- minicore: iterators
+//- minicore: iterators, copy
fn f(x: [(i32, u8); 10]) {
for (a, mut b) in x {
//^^^^^ 💡 weak: variable does not need to be mutable
@@ -565,8 +650,96 @@ fn f(x: [(i32, u8); 10]) {
}
#[test]
+ fn while_let() {
+ check_diagnostics(
+ r#"
+//- minicore: iterators, copy
+fn f(x: [(i32, u8); 10]) {
+ let mut it = x.into_iter();
+ while let Some((a, mut b)) = it.next() {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ while let Some((c, mut d)) = it.next() {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ a = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+ c = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `c`
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn index() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, index, slice
+fn f() {
+ let x = [1, 2, 3];
+ x[2] = 5;
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = &mut x;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let mut x = x;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x[2] = 5;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn overloaded_index() {
+ check_diagnostics(
+ r#"
+//- minicore: index
+use core::ops::{Index, IndexMut};
+
+struct Foo;
+impl Index<usize> for Foo {
+ type Output = (i32, u8);
+ fn index(&self, index: usize) -> &(i32, u8) {
+ &(5, 2)
+ }
+}
+impl IndexMut<usize> for Foo {
+ fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
+ &mut (5, 2)
+ }
+}
+fn f() {
+ let mut x = Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y = &x[2];
+ let x = Foo;
+ let y = &mut x[2];
+ //^💡 error: cannot mutate immutable variable `x`
+ let mut x = &mut Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y: &mut (i32, u8) = &mut x[2];
+ let x = Foo;
+ let ref mut y = x[7];
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let (ref mut y, _) = x[3];
+ //^ 💡 error: cannot mutate immutable variable `x`
+ match x[10] {
+ //^ 💡 error: cannot mutate immutable variable `x`
+ (ref y, _) => (),
+ (_, ref mut y) => (),
+ }
+ let mut x = Foo;
+ let mut i = 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y = &mut x[i];
+}
+"#,
+ );
+ }
+
+ #[test]
fn overloaded_deref() {
- // FIXME: check for false negative
check_diagnostics(
r#"
//- minicore: deref_mut
@@ -574,22 +747,36 @@ use core::ops::{Deref, DerefMut};
struct Foo;
impl Deref for Foo {
- type Target = i32;
- fn deref(&self) -> &i32 {
- &5
+ type Target = (i32, u8);
+ fn deref(&self) -> &(i32, u8) {
+ &(5, 2)
}
}
impl DerefMut for Foo {
- fn deref_mut(&mut self) -> &mut i32 {
- &mut 5
+ fn deref_mut(&mut self) -> &mut (i32, u8) {
+ &mut (5, 2)
}
}
fn f() {
- let x = Foo;
+ let mut x = Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
let y = &*x;
let x = Foo;
- let mut x = Foo;
- let y: &mut i32 = &mut x;
+ let y = &mut *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Foo;
+ let x = Foo;
+ let y: &mut (i32, u8) = &mut x;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let ref mut y = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let (ref mut y, _) = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ match *x {
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ (ref y, _) => (),
+ (_, ref mut y) => (),
+ }
}
"#,
);
@@ -632,6 +819,267 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
}
#[test]
+ // FIXME: We should have tests for `is_ty_uninhabited_from`
+ fn regression_14421() {
+ check_diagnostics(
+ r#"
+pub enum Tree {
+ Node(TreeNode),
+ Leaf(TreeLeaf),
+}
+
+struct Box<T>(&T);
+
+pub struct TreeNode {
+ pub depth: usize,
+ pub children: [Box<Tree>; 8]
+}
+
+pub struct TreeLeaf {
+ pub depth: usize,
+ pub data: u8
+}
+
+pub fn test() {
+ let mut tree = Tree::Leaf(
+ //^^^^^^^^ 💡 weak: variable does not need to be mutable
+ TreeLeaf {
+ depth: 0,
+ data: 0
+ }
+ );
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn fn_traits() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
+ x(2)
+ //^ 💡 error: cannot mutate immutable variable `x`
+}
+fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn closure() {
+ // FIXME: Diagnostic spans are inconsistent inside and outside closure
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn
+ struct X;
+
+ impl X {
+ fn mutate(&mut self) {}
+ }
+
+ fn f() {
+ let x = 5;
+ let closure1 = || { x = 2; };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let closure2 = || { x = x; };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let closure3 = || {
+ let x = 2;
+ x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+ x
+ };
+ let x = X;
+ let closure4 = || { x.mutate(); };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ }
+ "#,
+ );
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn
+ fn f() {
+ let mut x = 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let mut y = 2;
+ y = 7;
+ let closure = || {
+ let mut z = 8;
+ z = 3;
+ let mut k = z;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ };
+ }
+ "#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ let closure = || {
+ || {
+ || {
+ let x = 2;
+ || { || { x = 5; } }
+ //^ 💡 error: cannot mutate immutable variable `x`
+ }
+ }
+ };
+}
+ "#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ struct X;
+ let mut x = X;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let c1 = || x;
+ let mut x = X;
+ let c2 = || { x = X; x };
+ let mut x = X;
+ let c2 = move || { x = X; };
+}
+ "#,
+ );
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn, deref_mut
+ struct X(i32, i64);
+
+ fn f() {
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { *x = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { *x = 2; &x; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ let closure1 = || { *x = 2; &x; x = &mut 3; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = move || { *x = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut X(1, 2);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { x.0 = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn slice_pattern() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice, copy
+fn x(t: &[u8]) {
+ match t {
+ &[a, mut b] | &[a, _, mut b] => {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+
+ a = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+
+ }
+ _ => {}
+ }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn boxes() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice
+use core::ops::{Deref, DerefMut};
+use core::{marker::Unsize, ops::CoerceUnsized};
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized> {
+ inner: *mut T,
+}
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ #[rustc_box]
+ Box::new(t)
+ }
+}
+
+impl<T: ?Sized> Deref for Box<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &**self
+ }
+}
+
+impl<T: ?Sized> DerefMut for Box<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut **self
+ }
+}
+
+fn f() {
+ let x = Box::new(5);
+ x = Box::new(7);
+ //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Box::new(5);
+ *x = 7;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let mut y = Box::new(5);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ *x = *y;
+ //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Box::new(5);
+ let closure = || *x = 2;
+ //^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn allow_unused_mut_for_identifiers_starting_with_underline() {
+ check_diagnostics(
+ r#"
+fn f(_: i32) {}
+fn main() {
+ let mut _x = 2;
+ f(_x);
+}
+"#,
+ );
+ }
+
+ #[test]
fn respect_allow_unused_mut() {
// FIXME: respect
check_diagnostics(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 24c521ed1..a39eceab2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -21,7 +21,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.field.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.field.file_id);
missing_record_expr_field_fixes(
&ctx.sema,
d.field.file_id.original_file(ctx.sema.db),
@@ -69,7 +69,7 @@ fn missing_record_expr_field_fixes(
let new_field = make::record_field(
None,
make::name(record_expr_field.field_name()?.ident_token()?.text()),
- make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
+ make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?),
);
let last_field = record_fields.fields().last()?;
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index 67da5c7f2..4cd85a479 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -11,7 +11,11 @@ pub(crate) fn private_assoc_item(
d: &hir::PrivateAssocItem,
) -> Diagnostic {
// FIXME: add quickfix
- let name = d.item.name(ctx.sema.db).map(|name| format!("`{name}` ")).unwrap_or_default();
+ let name = d
+ .item
+ .name(ctx.sema.db)
+ .map(|name| format!("`{}` ", name.display(ctx.sema.db)))
+ .unwrap_or_default();
Diagnostic::new(
"private-assoc-item",
format!(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index be83ad6aa..de7f51f69 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -9,8 +9,8 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
"private-field",
format!(
"field `{}` of `{}` is private",
- d.field.name(ctx.sema.db),
- d.field.parent_def(ctx.sema.db).name(ctx.sema.db)
+ d.field.name(ctx.sema.db).display(ctx.sema.db),
+ d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 9b1c65983..d3eda3c5e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -28,7 +28,7 @@ fn fixes(
ctx: &DiagnosticsContext<'_>,
d: &hir::ReplaceFilterMapNextWithFindMap,
) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let next_expr = d.next_expr.to_node(&root);
let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
@@ -115,7 +115,7 @@ fn foo() {
r#"
//- minicore: iterators
fn foo() {
- let m = core::iter::repeat(())
+ let mut m = core::iter::repeat(())
.filter_map(|()| Some(92));
let n = m.next();
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4abc25a28..c28f98d83 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,5 @@
use either::Either;
-use hir::{db::ExpandDatabase, HirDisplay, InFile, Type};
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
ast::{self, BlockExpr, ExprStmt},
@@ -15,15 +15,25 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
let display_range = match &d.expr_or_pat {
- Either::Left(expr) => adjusted_display_range::<ast::BlockExpr>(
- ctx,
- expr.clone().map(|it| it.into()),
- &|block| {
- let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
- cov_mark::hit!(type_mismatch_on_block);
- Some(r_curly_range)
- },
- ),
+ Either::Left(expr) => {
+ adjusted_display_range::<ast::Expr>(ctx, expr.clone().map(|it| it.into()), &|expr| {
+ let salient_token_range = match expr {
+ ast::Expr::IfExpr(it) => it.if_token()?.text_range(),
+ ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(),
+ ast::Expr::ForExpr(it) => it.for_token()?.text_range(),
+ ast::Expr::WhileExpr(it) => it.while_token()?.text_range(),
+ ast::Expr::BlockExpr(it) => it.stmt_list()?.r_curly_token()?.text_range(),
+ ast::Expr::MatchExpr(it) => it.match_token()?.text_range(),
+ ast::Expr::MethodCallExpr(it) => it.name_ref()?.ident_token()?.text_range(),
+ ast::Expr::FieldExpr(it) => it.name_ref()?.ident_token()?.text_range(),
+ ast::Expr::AwaitExpr(it) => it.await_token()?.text_range(),
+ _ => return None,
+ };
+
+ cov_mark::hit!(type_mismatch_range_adjustment);
+ Some(salient_token_range)
+ })
+ }
Either::Right(pat) => {
ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
}
@@ -32,8 +42,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
"type-mismatch",
format!(
"expected {}, found {}",
- d.expected.display(ctx.sema.db),
- d.actual.display(ctx.sema.db)
+ d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+ d.actual.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
),
display_range,
)
@@ -93,7 +103,7 @@ fn add_missing_ok_or_some(
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let expr_range = expr.syntax().text_range();
let scope = ctx.sema.scope(expr.syntax())?;
@@ -133,7 +143,7 @@ fn remove_semicolon(
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
if !d.actual.is_unit() {
return None;
@@ -169,7 +179,7 @@ fn str_ref_to_owned(
return None;
}
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let expr_range = expr.syntax().text_range();
@@ -597,8 +607,21 @@ fn test() -> String {
}
#[test]
- fn type_mismatch_on_block() {
- cov_mark::check!(type_mismatch_on_block);
+ fn closure_mismatch_show_different_type() {
+ check_diagnostics(
+ r#"
+fn f() {
+ let mut x = (|| 1, 2);
+ x = (|| 3, 4);
+ //^^^^ error: expected {closure#0}, found {closure#1}
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn type_mismatch_range_adjustment() {
+ cov_mark::check!(type_mismatch_range_adjustment);
check_diagnostics(
r#"
fn f() -> i32 {
@@ -607,6 +630,57 @@ fn f() -> i32 {
let _ = x + y;
}
//^ error: expected i32, found ()
+
+fn g() -> i32 {
+ while true {}
+} //^^^^^ error: expected i32, found ()
+
+struct S;
+impl S { fn foo(&self) -> &S { self } }
+fn h() {
+ let _: i32 = S.foo().foo().foo();
+} //^^^ error: expected i32, found &S
+"#,
+ );
+ }
+
+ #[test]
+ fn unknown_type_in_function_signature() {
+ check_diagnostics(
+ r#"
+struct X<T>(T);
+
+fn foo(x: X<Unknown>) {}
+fn test1() {
+ // Unknown might be `i32`, so we should not emit type mismatch here.
+ foo(X(42));
+}
+fn test2() {
+ foo(42);
+ //^^ error: expected X<{unknown}>, found i32
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn evaluate_const_generics_in_types() {
+ check_diagnostics(
+ r#"
+pub const ONE: usize = 1;
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<2>(),
+ //^^^^^^^^^^^^ error: expected Inner<1>, found Inner<2>
+ };
+}
"#,
);
}
@@ -617,12 +691,49 @@ fn f() -> i32 {
r#"
fn f() {
let &() = &mut ();
+ //^^^ error: expected &mut (), found &()
match &() {
+ // FIXME: we should only show the deep one.
&9 => ()
+ //^^ error: expected &(), found &i32
//^ error: expected (), found i32
}
}
"#,
);
}
+
+ #[test]
+ fn regression_14768() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, fmt, slice, coerce_unsized, builtin_impls
+use core::fmt::Debug;
+
+#[derive(Debug)]
+struct Foo(u8, u16, [u8]);
+
+#[derive(Debug)]
+struct Bar {
+ f1: u8,
+ f2: &[u16],
+ f3: dyn Debug,
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn return_no_value() {
+ check_diagnostics(
+ r#"
+fn f() -> i32 {
+ return;
+ // ^^^^^^ error: expected i32, found ()
+ 0
+}
+fn g() { return; }
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
new file mode 100644
index 000000000..e12bbcf68
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -0,0 +1,232 @@
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, StructKind};
+use ide_db::{
+ assists::{Assist, AssistId, AssistKind, GroupLabel},
+ label::Label,
+ source_change::SourceChange,
+};
+use syntax::AstNode;
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: typed-hole
+//
+// This diagnostic is triggered when an underscore expression is used in an invalid position.
+pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Diagnostic {
+ let display_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into()));
+ let (message, fixes) = if d.expected.is_unknown() {
+ ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None)
+ } else {
+ (
+ format!(
+ "invalid `_` expression, expected type `{}`",
+ d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+ ),
+ fixes(ctx, d),
+ )
+ };
+
+ Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes)
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
+ let db = ctx.sema.db;
+ let root = db.parse_or_expand(d.expr.file_id);
+ let original_range =
+ d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
+ let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?;
+ let mut assists = vec![];
+ scope.process_all_names(&mut |name, def| {
+ let ty = match def {
+ hir::ScopeDef::ModuleDef(it) => match it {
+ hir::ModuleDef::Function(it) => it.ty(db),
+ hir::ModuleDef::Adt(hir::Adt::Struct(it)) if it.kind(db) != StructKind::Record => {
+ it.constructor_ty(db)
+ }
+ hir::ModuleDef::Variant(it) if it.kind(db) != StructKind::Record => {
+ it.constructor_ty(db)
+ }
+ hir::ModuleDef::Const(it) => it.ty(db),
+ hir::ModuleDef::Static(it) => it.ty(db),
+ _ => return,
+ },
+ hir::ScopeDef::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
+ hir::ScopeDef::Local(it) => it.ty(db),
+ _ => return,
+ };
+ // FIXME: should also check coercions if it is at a coercion site
+ if !ty.contains_unknown() && ty.could_unify_with(db, &d.expected) {
+ assists.push(Assist {
+ id: AssistId("typed-hole", AssistKind::QuickFix),
+ label: Label::new(format!("Replace `_` with `{}`", name.display(db))),
+ group: Some(GroupLabel("Replace `_` with a matching entity in scope".to_owned())),
+ target: original_range.range,
+ source_change: Some(SourceChange::from_text_edit(
+ original_range.file_id,
+ TextEdit::replace(original_range.range, name.display(db).to_string()),
+ )),
+ trigger_signature_help: false,
+ });
+ }
+ });
+ if assists.is_empty() {
+ None
+ } else {
+ Some(assists)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_diagnostics, check_fixes};
+
+ #[test]
+ fn unknown() {
+ check_diagnostics(
+ r#"
+fn main() {
+ _;
+ //^ error: `_` expressions may only appear on the left-hand side of an assignment
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn concrete_expectation() {
+ check_diagnostics(
+ r#"
+fn main() {
+ if _ {}
+ //^ error: invalid `_` expression, expected type `bool`
+ let _: fn() -> i32 = _;
+ //^ error: invalid `_` expression, expected type `fn() -> i32`
+ let _: fn() -> () = _; // FIXME: This should trigger an assist because `main` matches via *coercion*
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn integer_ty_var() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let mut x = 3;
+ x = _;
+ //^ 💡 error: invalid `_` expression, expected type `i32`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn ty_var_resolved() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let mut x = t();
+ x = _;
+ //^ 💡 error: invalid `_` expression, expected type `&str`
+ x = "";
+}
+fn t<T>() -> T { loop {} }
+"#,
+ );
+ }
+
+ #[test]
+ fn valid_positions() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let x = [(); _];
+ let y: [(); 10] = [(); _];
+ _ = 0;
+ (_,) = (1,);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn check_quick_fix() {
+ check_fixes(
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = _$0;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ vec![
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = local;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = param;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = CP;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = Bar;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = C;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ ],
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
new file mode 100644
index 000000000..034e4fcfb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -0,0 +1,88 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: undeclared-label
+pub(crate) fn undeclared_label(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::UndeclaredLabel,
+) -> Diagnostic {
+ let name = &d.name;
+ Diagnostic::new(
+ "undeclared-label",
+ format!("use of undeclared label `{}`", name.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn smoke_test() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ //^^ error: use of undeclared label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ //^^ error: use of undeclared label `'a`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn for_loop() {
+ check_diagnostics(
+ r#"
+//- minicore: iterator
+fn foo() {
+ 'xxx: for _ in unknown {
+ 'yyy: for _ in unknown {
+ break 'xxx;
+ continue 'yyy;
+ break 'zzz;
+ //^^^^ error: use of undeclared label `'zzz`
+ }
+ continue 'xxx;
+ continue 'yyy;
+ //^^^^ error: use of undeclared label `'yyy`
+ break 'xxx;
+ break 'yyy;
+ //^^^^ error: use of undeclared label `'yyy`
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn try_operator_desugar_works() {
+ check_diagnostics(
+ r#"
+//- minicore: option, try
+fn foo() {
+ None?;
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: option, try, future
+async fn foo() {
+ None?;
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: option, try, future, fn
+async fn foo() {
+ || None?;
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 3d45a7591..271e7ce73 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -2,7 +2,7 @@
use std::iter;
-use hir::{db::DefDatabase, InFile, ModuleSource};
+use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
use ide_db::{
base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
source_change::SourceChange,
@@ -10,7 +10,7 @@ use ide_db::{
};
use syntax::{
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
- AstNode, TextRange, TextSize,
+ AstNode, TextRange,
};
use text_edit::TextEdit;
@@ -27,14 +27,28 @@ pub(crate) fn unlinked_file(
) {
// Limit diagnostic to the first few characters in the file. This matches how VS Code
// renders it with the full span, but on other editors, and is less invasive.
+ let fixes = fixes(ctx, file_id);
+ // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
+ // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523
+ let message = if fixes.is_none() {
+ "file not included in crate hierarchy"
+ } else {
+ "file not included in module tree"
+ };
+
let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
- // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
- let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
+ let range = FileLoader::file_text(ctx.sema.db, file_id)
+ .char_indices()
+ .take(3)
+ .last()
+ .map(|(i, _)| i)
+ .map(|i| TextRange::up_to(i.try_into().unwrap()))
+ .unwrap_or(range);
acc.push(
- Diagnostic::new("unlinked-file", "file not included in module tree", range)
+ Diagnostic::new("unlinked-file", message, range)
.severity(Severity::WeakWarning)
- .with_fixes(fixes(ctx, file_id)),
+ .with_fixes(fixes),
);
}
@@ -60,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
'crates: for &krate in &*ctx.sema.db.relevant_crates(file_id) {
let crate_def_map = ctx.sema.db.crate_def_map(krate);
- let root_module = &crate_def_map[crate_def_map.root()];
+ let root_module = &crate_def_map[DefMap::ROOT];
let Some(root_file_id) = root_module.origin.file_id() else { continue };
let Some(crate_root_path) = source_root.path_for_file(&root_file_id) else { continue };
let Some(rel) = parent.strip_prefix(&crate_root_path.parent()?) else { continue };
@@ -92,7 +106,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
// if we aren't adding to a crate root, walk backwards such that we support `#[path = ...]` overrides if possible
// build all parent paths of the form `../module_name/mod.rs` and `../module_name.rs`
- let paths = iter::successors(Some(parent.clone()), |prev| prev.parent()).filter_map(|path| {
+ let paths = iter::successors(Some(parent), |prev| prev.parent()).filter_map(|path| {
let parent = path.parent()?;
let (name, _) = path.name_and_extension()?;
Some(([parent.join(&format!("{name}.rs"))?, path.join("mod.rs")?], name.to_owned()))
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
new file mode 100644
index 000000000..9fedadeae
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -0,0 +1,91 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unreachable-label
+pub(crate) fn unreachable_label(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::UnreachableLabel,
+) -> Diagnostic {
+ let name = &d.name;
+ Diagnostic::new(
+ "unreachable-label",
+ format!("use of unreachable label `{}`", name.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn async_blocks_are_borders() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ async {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ // ^^ error: use of unreachable label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ // ^^ error: use of unreachable label `'a`
+ };
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn closures_are_borders() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ || {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ // ^^ error: use of unreachable label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ // ^^ error: use of unreachable label `'a`
+ };
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn blocks_pass_through() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ {
+ break 'a;
+ continue 'a;
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn try_blocks_pass_through() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ try {
+ break 'a;
+ continue 'a;
+ };
+ }
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index cefa74e52..5e4efa41f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -26,7 +26,7 @@ pub(crate) fn unresolved_field(
"unresolved-field",
format!(
"no field `{}` on type `{}`{method_suffix}",
- d.name,
+ d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
@@ -45,12 +45,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<A
}
}
-// FIXME: We should fill out the call here, mvoe the cursor and trigger signature help
+// FIXME: We should fill out the call here, move the cursor and trigger signature help
fn method_fix(
ctx: &DiagnosticsContext<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
Some(vec![Assist {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 1a5efff2c..3943b51ab 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -13,7 +13,7 @@ pub(crate) fn unresolved_macro_call(
let bang = if d.is_bang { "!" } else { "" };
Diagnostic::new(
"unresolved-macro-call",
- format!("unresolved macro `{}{bang}`", d.path),
+ format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)),
display_range,
)
.experimental()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index f3ec6efa7..8bbb837e6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -26,7 +26,7 @@ pub(crate) fn unresolved_method(
"unresolved-method",
format!(
"no method `{}` on type `{}`{field_suffix}",
- d.name,
+ d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
@@ -53,7 +53,7 @@ fn field_fix(
return None;
}
let expr_ptr = &d.expr;
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let (file_id, range) = match expr {
ast::Expr::MethodCallExpr(mcall) => {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
index 94614f11c..6e3fd3b42 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
@@ -31,7 +31,7 @@ pub(crate) fn unresolved_module(
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.decl.file_id);
let unresolved_module = d.decl.value.to_node(&root);
Some(
d.candidates
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
index 9a984ba6b..ae5cf1358 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -25,25 +25,21 @@ pub(crate) fn unresolved_proc_macro(
_ => proc_macros_enabled,
};
- let message = match &d.macro_name {
+ let not_expanded_message = match &d.macro_name {
Some(name) => format!("proc macro `{name}` not expanded"),
None => "proc macro not expanded".to_string(),
};
let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning };
let def_map = ctx.sema.db.crate_def_map(d.krate);
- let message = format!(
- "{message}: {}",
- if config_enabled {
- def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib")
- } else {
- match d.kind {
- hir::MacroKind::Attr if proc_macros_enabled => {
- "attribute macro expansion is disabled"
- }
- _ => "proc-macro expansion is disabled",
- }
- },
- );
+ let message = if config_enabled {
+ def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib")
+ } else {
+ match d.kind {
+ hir::MacroKind::Attr if proc_macros_enabled => "attribute macro expansion is disabled",
+ _ => "proc-macro expansion is disabled",
+ }
+ };
+ let message = format!("{not_expanded_message}: {message}");
Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 71f136b8c..55a4a482d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -38,11 +38,13 @@ mod handlers {
pub(crate) mod missing_fields;
pub(crate) mod missing_match_arms;
pub(crate) mod missing_unsafe;
+ pub(crate) mod moved_out_of_ref;
pub(crate) mod mutability_errors;
pub(crate) mod no_such_field;
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
pub(crate) mod replace_filter_map_next_with_find_map;
+ pub(crate) mod typed_hole;
pub(crate) mod type_mismatch;
pub(crate) mod unimplemented_builtin_macro;
pub(crate) mod unresolved_extern_crate;
@@ -52,6 +54,8 @@ mod handlers {
pub(crate) mod unresolved_macro_call;
pub(crate) mod unresolved_module;
pub(crate) mod unresolved_proc_macro;
+ pub(crate) mod undeclared_label;
+ pub(crate) mod unreachable_label;
// The handlers below are unusual, the implement the diagnostics as well.
pub(crate) mod field_shorthand;
@@ -74,6 +78,7 @@ use ide_db::{
};
use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
+// FIXME: Make this an enum
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DiagnosticCode(pub &'static str);
@@ -198,7 +203,7 @@ impl<'a> DiagnosticsContext<'a> {
let sema = &self.sema;
(|| {
let precise_location = precise_location?;
- let root = sema.parse_or_expand(node.file_id)?;
+ let root = sema.parse_or_expand(node.file_id);
match root.covering_element(precise_location) {
syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)),
syntax::NodeOrToken::Token(it) => {
@@ -246,42 +251,60 @@ pub fn diagnostics(
let mut diags = Vec::new();
if let Some(m) = module {
- m.diagnostics(db, &mut diags)
+ m.diagnostics(db, &mut diags);
}
for diag in diags {
#[rustfmt::skip]
let d = match diag {
- AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
- AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+ AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
+ Some(it) => it,
+ None => continue,
+ }
AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
+ AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+ AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
+ AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
+ AnyDiagnostic::MacroExpansionParseError(d) => {
+ res.extend(d.errors.iter().take(32).map(|err| {
+ {
+ Diagnostic::new(
+ "syntax-error",
+ format!("Syntax Error in Expansion: {err}"),
+ ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
+ )
+ }
+ .experimental()
+ }));
+ continue;
+ },
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
+ AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
+ AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
+ AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
+ AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),
AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
+ AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label:: unreachable_label(&ctx, &d),
AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
+ AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
+ AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
- AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
- AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
- AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
- AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
- AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
- Some(it) => it,
- None => continue,
- }
+ AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
};
res.push(d)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
index afa641c73..b5cd4e0d6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -8,7 +8,7 @@ use ide_db::{
RootDatabase,
};
use stdx::trim_indent;
-use test_utils::{assert_eq_text, extract_annotations};
+use test_utils::{assert_eq_text, extract_annotations, MiniCore};
use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
@@ -121,6 +121,15 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
})
.collect::<Vec<_>>();
actual.sort_by_key(|(range, _)| range.start());
+ if expected.is_empty() {
+ // makes minicore smoke test debugable
+ for (e, _) in &actual {
+ eprintln!(
+ "Code in range {e:?} = {}",
+ &db.file_text(file_id)[usize::from(e.start())..usize::from(e.end())]
+ )
+ }
+ }
assert_eq!(expected, actual);
}
}
@@ -143,3 +152,28 @@ fn test_disabled_diagnostics() {
);
assert!(!diagnostics.is_empty());
}
+
+#[test]
+fn minicore_smoke_test() {
+ fn check(minicore: MiniCore) {
+ let source = minicore.source_code();
+ let mut config = DiagnosticsConfig::test_sample();
+ // This should be ignored since we conditionaly remove code which creates single item use with braces
+ config.disabled.insert("unnecessary-braces".to_string());
+ check_diagnostics_with_config(config, &source);
+ }
+
+ // Checks that there is no diagnostic in minicore for each flag.
+ for flag in MiniCore::available_flags() {
+ if flag == "clone" {
+ // Clone without copy has `moved-out-of-ref`, so ignoring.
+ // FIXME: Maybe we should merge copy and clone in a single flag?
+ continue;
+ }
+ eprintln!("Checking minicore flag {flag}");
+ check(MiniCore::from_flags([flag]));
+ }
+ // And one time for all flags, to check codes which are behind multiple flags + prevent name collisions
+ eprintln!("Checking all minicore flags");
+ check(MiniCore::from_flags(MiniCore::available_flags()))
+}
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
index 04efa7b91..70ed6dea5 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
@@ -15,6 +15,8 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
itertools = "0.10.5"
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
hir.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
index d9834ee63..66832a0be 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
@@ -87,8 +87,8 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc
use crate::{errors::bail, matching::MatchFailureReason};
use hir::Semantics;
use ide_db::base_db::{FileId, FilePosition, FileRange};
+use nohash_hasher::IntMap;
use resolving::ResolvedRule;
-use stdx::hash::NoHashHashMap;
use syntax::{ast, AstNode, SyntaxNode, TextRange};
use text_edit::TextEdit;
@@ -168,9 +168,9 @@ impl<'db> MatchFinder<'db> {
}
/// Finds matches for all added rules and returns edits for all found matches.
- pub fn edits(&self) -> NoHashHashMap<FileId, TextEdit> {
+ pub fn edits(&self) -> IntMap<FileId, TextEdit> {
use ide_db::base_db::SourceDatabaseExt;
- let mut matches_by_file = NoHashHashMap::default();
+ let mut matches_by_file = IntMap::default();
for m in self.matches().matches {
matches_by_file
.entry(m.range.file_id)
@@ -184,6 +184,7 @@ impl<'db> MatchFinder<'db> {
(
file_id,
replacing::matches_to_edit(
+ self.sema.db,
&matches,
&self.sema.db.file_text(file_id),
&self.rules,
@@ -224,7 +225,7 @@ impl<'db> MatchFinder<'db> {
let file = self.sema.parse(file_id);
let mut res = Vec::new();
let file_text = self.sema.db.file_text(file_id);
- let mut remaining_text = file_text.as_str();
+ let mut remaining_text = &*file_text;
let mut base = 0;
let len = snippet.len() as u32;
while let Some(offset) = remaining_text.find(snippet) {
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
index e27ef6e35..b4b83f62d 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
@@ -14,14 +14,16 @@ use crate::{fragments, resolving::ResolvedRule, Match, SsrMatches};
/// template. Placeholders in the template will have been substituted with whatever they matched to
/// in the original code.
pub(crate) fn matches_to_edit(
+ db: &dyn hir::db::ExpandDatabase,
matches: &SsrMatches,
file_src: &str,
rules: &[ResolvedRule],
) -> TextEdit {
- matches_to_edit_at_offset(matches, file_src, 0.into(), rules)
+ matches_to_edit_at_offset(db, matches, file_src, 0.into(), rules)
}
fn matches_to_edit_at_offset(
+ db: &dyn hir::db::ExpandDatabase,
matches: &SsrMatches,
file_src: &str,
relative_start: TextSize,
@@ -31,13 +33,14 @@ fn matches_to_edit_at_offset(
for m in &matches.matches {
edit_builder.replace(
m.range.range.checked_sub(relative_start).unwrap(),
- render_replace(m, file_src, rules),
+ render_replace(db, m, file_src, rules),
);
}
edit_builder.finish()
}
struct ReplacementRenderer<'a> {
+ db: &'a dyn hir::db::ExpandDatabase,
match_info: &'a Match,
file_src: &'a str,
rules: &'a [ResolvedRule],
@@ -53,13 +56,19 @@ struct ReplacementRenderer<'a> {
placeholder_tokens_requiring_parenthesis: FxHashSet<SyntaxToken>,
}
-fn render_replace(match_info: &Match, file_src: &str, rules: &[ResolvedRule]) -> String {
+fn render_replace(
+ db: &dyn hir::db::ExpandDatabase,
+ match_info: &Match,
+ file_src: &str,
+ rules: &[ResolvedRule],
+) -> String {
let rule = &rules[match_info.rule_index];
let template = rule
.template
.as_ref()
.expect("You called MatchFinder::edits after calling MatchFinder::add_search_pattern");
let mut renderer = ReplacementRenderer {
+ db,
match_info,
file_src,
rules,
@@ -96,7 +105,7 @@ impl ReplacementRenderer<'_> {
fn render_node(&mut self, node: &SyntaxNode) {
if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) {
- self.out.push_str(&mod_path.to_string());
+ self.out.push_str(&mod_path.display(self.db).to_string());
// Emit everything except for the segment's name-ref, since we already effectively
// emitted that as part of `mod_path`.
if let Some(path) = ast::Path::cast(node.clone()) {
@@ -144,6 +153,7 @@ impl ReplacementRenderer<'_> {
);
}
let edit = matches_to_edit_at_offset(
+ self.db,
&placeholder_value.inner_matches,
self.file_src,
range.start(),
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
index 61698fca8..424ba3d7f 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
@@ -3,8 +3,8 @@ use ide_db::{
base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt},
FxHashSet,
};
-use std::sync::Arc;
use test_utils::RangeOrOffset;
+use triomphe::Arc;
use crate::{MatchFinder, SsrRule};
diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml
index 30e514e41..2aee203c4 100644
--- a/src/tools/rust-analyzer/crates/ide/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml
@@ -23,6 +23,8 @@ pulldown-cmark = { version = "0.9.1", default-features = false }
url = "2.3.1"
dot = "0.1.4"
smallvec.workspace = true
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 48bcd37b6..dd1d0d75c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -263,7 +263,7 @@ mod tests {
expect![["callee Function FileId(0) 0..14 3..9"]],
expect![[r#"
caller1 Function FileId(0) 15..45 18..25 : [34..40]
- test_caller Function FileId(0) 95..149 110..121 : [134..140]"#]],
+ test_caller Function FileId(0) 95..149 110..121 tests : [134..140]"#]],
expect![[]],
);
}
@@ -283,7 +283,7 @@ fn caller() {
//- /foo/mod.rs
pub fn callee() {}
"#,
- expect![["callee Function FileId(1) 0..18 7..13"]],
+ expect!["callee Function FileId(1) 0..18 7..13 foo"],
expect![["caller Function FileId(0) 27..56 30..36 : [45..51]"]],
expect![[]],
);
@@ -323,7 +323,7 @@ pub fn callee() {}
"#,
expect![["caller Function FileId(0) 27..56 30..36"]],
expect![[]],
- expect![["callee Function FileId(1) 0..18 7..13 : [45..51]"]],
+ expect!["callee Function FileId(1) 0..18 7..13 foo : [45..51]"],
);
}
@@ -477,7 +477,7 @@ fn caller() {
S1::callee();
}
"#,
- expect![["callee Function FileId(0) 15..27 18..24"]],
+ expect!["callee Function FileId(0) 15..27 18..24 T1"],
expect![["caller Function FileId(0) 82..115 85..91 : [104..110]"]],
expect![[]],
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index fae25f310..8112c4f72 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -5,6 +5,8 @@ mod tests;
mod intra_doc_links;
+use std::ffi::OsStr;
+
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
use stdx::format_to;
@@ -12,7 +14,7 @@ use url::Url;
use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs};
use ide_db::{
- base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase},
+ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
RootDatabase,
@@ -29,8 +31,16 @@ use crate::{
FilePosition, Semantics,
};
-/// Weblink to an item's documentation.
-pub(crate) type DocumentationLink = String;
+/// Web and local links to an item's documentation.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct DocumentationLinks {
+ /// The URL to the documentation on docs.rs.
+ /// May not lead anywhere.
+ pub web_url: Option<String>,
+ /// The URL to the documentation in the local file system.
+ /// May not lead anywhere.
+ pub local_url: Option<String>,
+}
const MARKDOWN_OPTIONS: Options =
Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS);
@@ -109,7 +119,7 @@ pub(crate) fn remove_links(markdown: &str) -> String {
// Feature: Open Docs
//
-// Retrieve a link to documentation for the given symbol.
+// Retrieve a links to documentation for the given symbol.
//
// The simplest way to use this feature is via the context menu. Right-click on
// the selected item. The context menu opens. Select **Open Docs**.
@@ -122,7 +132,9 @@ pub(crate) fn remove_links(markdown: &str) -> String {
pub(crate) fn external_docs(
db: &RootDatabase,
position: &FilePosition,
-) -> Option<DocumentationLink> {
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> Option<DocumentationLinks> {
let sema = &Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone();
let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
@@ -146,11 +158,11 @@ pub(crate) fn external_docs(
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
},
- _ => return None,
+ _ => return None
}
};
- get_doc_link(db, definition)
+ Some(get_doc_links(db, definition, target_dir, sysroot))
}
/// Extracts all links from a given markdown text returning the definition text range, link-text
@@ -308,19 +320,35 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)
//
// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented
// https://github.com/rust-lang/rfcs/pull/2988
-fn get_doc_link(db: &RootDatabase, def: Definition) -> Option<String> {
- let (target, file, frag) = filename_and_frag_for_def(db, def)?;
+fn get_doc_links(
+ db: &RootDatabase,
+ def: Definition,
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> DocumentationLinks {
+ let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
+ base_url.and_then(|url| url.join(path).ok())
+ };
+
+ let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); };
- let mut url = get_doc_base_url(db, target)?;
+ let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot);
if let Some(path) = mod_path_of_def(db, target) {
- url = url.join(&path).ok()?;
+ web_url = join_url(web_url, &path);
+ local_url = join_url(local_url, &path);
}
- url = url.join(&file).ok()?;
- url.set_fragment(frag.as_deref());
+ web_url = join_url(web_url, &file);
+ local_url = join_url(local_url, &file);
- Some(url.into())
+ web_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+ local_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+
+ DocumentationLinks {
+ web_url: web_url.map(|it| it.into()),
+ local_url: local_url.map(|it| it.into()),
+ }
}
fn rewrite_intra_doc_link(
@@ -332,7 +360,7 @@ fn rewrite_intra_doc_link(
let (link, ns) = parse_intra_doc_link(target);
let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
- let mut url = get_doc_base_url(db, resolved)?;
+ let mut url = get_doc_base_urls(db, resolved, None, None).0?;
let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
if let Some(path) = mod_path_of_def(db, resolved) {
@@ -351,7 +379,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
return None;
}
- let mut url = get_doc_base_url(db, def)?;
+ let mut url = get_doc_base_urls(db, def, None, None).0?;
let (def, file, frag) = filename_and_frag_for_def(db, def)?;
if let Some(path) = mod_path_of_def(db, def) {
@@ -366,7 +394,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
def.canonical_module_path(db).map(|it| {
let mut path = String::new();
- it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
+ it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db)));
path
})
}
@@ -426,18 +454,38 @@ fn map_links<'e>(
/// ```ignore
/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^
+/// file:///project/root/target/doc/std/iter/trait.Iterator.html#tymethod.next
+/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// ```
-fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
+fn get_doc_base_urls(
+ db: &RootDatabase,
+ def: Definition,
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> (Option<Url>, Option<Url>) {
+ let local_doc = target_dir
+ .and_then(|path| path.to_str())
+ .and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
+ .and_then(|it| it.join("doc/").ok());
+ let system_doc = sysroot
+ .and_then(|it| it.to_str())
+ .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
+ .and_then(|it| Url::parse(&it).ok());
+
// special case base url of `BuiltinType` to core
// https://github.com/rust-lang/rust-analyzer/issues/12250
if let Definition::BuiltinType(..) = def {
- return Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+ let web_link = Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+ let system_link = system_doc.and_then(|it| it.join("core/").ok());
+ return (web_link, system_link);
};
- let krate = def.krate(db)?;
- let display_name = krate.display_name(db)?;
+ let Some(krate) = def.krate(db) else { return Default::default() };
+ let Some(display_name) = krate.display_name(db) else { return Default::default() };
+ let crate_data = &db.crate_graph()[krate.into()];
+ let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str);
- let base = match db.crate_graph()[krate.into()].origin {
+ let (web_base, local_base) = match &crate_data.origin {
// std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.
// FIXME: Use the toolchains channel instead of nightly
CrateOrigin::Lang(
@@ -447,10 +495,17 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
| LangCrateOrigin::Std
| LangCrateOrigin::Test),
) => {
- format!("https://doc.rust-lang.org/nightly/{origin}")
+ let system_url = system_doc.and_then(|it| it.join(&format!("{origin}")).ok());
+ let web_url = format!("https://doc.rust-lang.org/{channel}/{origin}");
+ (Some(web_url), system_url)
}
- _ => {
- krate.get_html_root_url(db).or_else(|| {
+ CrateOrigin::Lang(_) => return (None, None),
+ CrateOrigin::Rustc { name: _ } => {
+ (Some(format!("https://doc.rust-lang.org/{channel}/nightly-rustc/")), None)
+ }
+ CrateOrigin::Local { repo: _, name: _ } => {
+ // FIXME: These should not attempt to link to docs.rs!
+ let weblink = krate.get_html_root_url(db).or_else(|| {
let version = krate.version(db);
// Fallback to docs.rs. This uses `display_name` and can never be
// correct, but that's what fallbacks are about.
@@ -462,10 +517,32 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
krate = display_name,
version = version.as_deref().unwrap_or("*")
))
- })?
+ });
+ (weblink, local_doc)
+ }
+ CrateOrigin::Library { repo: _, name } => {
+ let weblink = krate.get_html_root_url(db).or_else(|| {
+ let version = krate.version(db);
+ // Fallback to docs.rs. This uses `display_name` and can never be
+ // correct, but that's what fallbacks are about.
+ //
+ // FIXME: clicking on the link should just open the file in the editor,
+ // instead of falling back to external urls.
+ Some(format!(
+ "https://docs.rs/{krate}/{version}/",
+ krate = name,
+ version = version.as_deref().unwrap_or("*")
+ ))
+ });
+ (weblink, local_doc)
}
};
- Url::parse(&base).ok()?.join(&format!("{display_name}/")).ok()
+ let web_base = web_base
+ .and_then(|it| Url::parse(&it).ok())
+ .and_then(|it| it.join(&format!("{display_name}/")).ok());
+ let local_base = local_base.and_then(|it| it.join(&format!("{display_name}/")).ok());
+
+ (web_base, local_base)
}
/// Get the filename and extension generated for a symbol by rustdoc.
@@ -490,9 +567,9 @@ fn filename_and_frag_for_def(
let res = match def {
Definition::Adt(adt) => match adt {
- Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
- Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
- Adt::Union(u) => format!("union.{}.html", u.name(db)),
+ Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())),
+ Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())),
+ Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())),
},
Definition::Module(m) => match m.name(db) {
// `#[doc(keyword = "...")]` is internal used only by rust compiler
@@ -500,21 +577,25 @@ fn filename_and_frag_for_def(
Some(kw) => {
format!("keyword.{}.html", kw.trim_matches('"'))
}
- None => format!("{name}/index.html"),
+ None => format!("{}/index.html", name.display(db.upcast())),
},
None => String::from("index.html"),
},
- Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
- Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
- Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
- Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
- Definition::Function(f) => format!("fn.{}.html", f.name(db)),
+ Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())),
+ Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())),
+ Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())),
+ Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())),
+ Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())),
Definition::Variant(ev) => {
- format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
+ format!(
+ "enum.{}.html#variant.{}",
+ ev.parent_enum(db).name(db).display(db.upcast()),
+ ev.name(db).display(db.upcast())
+ )
}
- Definition::Const(c) => format!("const.{}.html", c.name(db)?),
- Definition::Static(s) => format!("static.{}.html", s.name(db)),
- Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)),
+ Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())),
+ Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())),
+ Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())),
Definition::Field(field) => {
let def = match field.parent_def(db) {
hir::VariantDef::Struct(it) => Definition::Adt(it.into()),
@@ -522,7 +603,11 @@ fn filename_and_frag_for_def(
hir::VariantDef::Variant(it) => Definition::Variant(it),
};
let (_, file, _) = filename_and_frag_for_def(db, def)?;
- return Some((def, file, Some(format!("structfield.{}", field.name(db)))));
+ return Some((
+ def,
+ file,
+ Some(format!("structfield.{}", field.name(db).display(db.upcast()))),
+ ));
}
Definition::SelfType(impl_) => {
let adt = impl_.self_ty(db).as_adt()?.into();
@@ -556,12 +641,14 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) ->
// Rustdoc makes this decision based on whether a method 'has defaultness'.
// Currently this is only the case for provided trait methods.
if is_trait_method && !function.has_body(db) {
- format!("tymethod.{}", function.name(db))
+ format!("tymethod.{}", function.name(db).display(db.upcast()))
} else {
- format!("method.{}", function.name(db))
+ format!("method.{}", function.name(db).display(db.upcast()))
}
}
- AssocItem::Const(constant) => format!("associatedconstant.{}", constant.name(db)?),
- AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db)),
+ AssocItem::Const(constant) => {
+ format!("associatedconstant.{}", constant.name(db)?.display(db.upcast()))
+ }
+ AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())),
})
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 104181a33..05a64b33b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,3 +1,5 @@
+use std::ffi::OsStr;
+
use expect_test::{expect, Expect};
use hir::{HasAttrs, Semantics};
use ide_db::{
@@ -13,11 +15,33 @@ use crate::{
fixture, TryToNav,
};
-fn check_external_docs(ra_fixture: &str, expect: Expect) {
+fn check_external_docs(
+ ra_fixture: &str,
+ target_dir: Option<&OsStr>,
+ expect_web_url: Option<Expect>,
+ expect_local_url: Option<Expect>,
+ sysroot: Option<&OsStr>,
+) {
let (analysis, position) = fixture::position(ra_fixture);
- let url = analysis.external_docs(position).unwrap().expect("could not find url for symbol");
+ let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
+
+ let web_url = links.web_url;
+ let local_url = links.local_url;
+
+ println!("web_url: {:?}", web_url);
+ println!("local_url: {:?}", local_url);
+
+ match (expect_web_url, web_url) {
+ (Some(expect), Some(url)) => expect.assert_eq(&url),
+ (None, None) => (),
+ _ => panic!("Unexpected web url"),
+ }
- expect.assert_eq(&url)
+ match (expect_local_url, local_url) {
+ (Some(expect), Some(url)) => expect.assert_eq(&url),
+ (None, None) => (),
+ _ => panic!("Unexpected local url"),
+ }
}
fn check_rewrite(ra_fixture: &str, expect: Expect) {
@@ -97,6 +121,20 @@ fn node_to_def(
}
#[test]
+fn external_docs_doc_builtin_type() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+let x: u3$02 = 0;
+"#,
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
+ Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
fn external_docs_doc_url_crate() {
check_external_docs(
r#"
@@ -105,7 +143,10 @@ use foo$0::Foo;
//- /lib.rs crate:foo
pub struct Foo;
"#,
- expect![[r#"https://docs.rs/foo/*/foo/index.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
+ Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -116,7 +157,10 @@ fn external_docs_doc_url_std_crate() {
//- /main.rs crate:std
use self$0;
"#,
- expect![[r#"https://doc.rust-lang.org/nightly/std/index.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
+ Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -127,7 +171,38 @@ fn external_docs_doc_url_struct() {
//- /main.rs crate:foo
pub struct Fo$0o;
"#,
- expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
+fn external_docs_doc_url_windows_backslash_path() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+ Some(&OsStr::new(r"C:\Users\user\project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
+fn external_docs_doc_url_windows_slash_path() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+ Some(&OsStr::new(r"C:/Users/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -140,7 +215,10 @@ pub struct Foo {
field$0: ()
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]]),
+ None,
+ None,
);
}
@@ -151,7 +229,10 @@ fn external_docs_doc_url_fn() {
//- /main.rs crate:foo
pub fn fo$0o() {}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]]),
+ None,
+ None,
);
}
@@ -165,7 +246,10 @@ impl Foo {
pub fn method$0() {}
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -175,7 +259,10 @@ impl Foo {
const CONST$0: () = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
}
@@ -192,7 +279,10 @@ impl Trait for Foo {
pub fn method$0() {}
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -205,7 +295,10 @@ impl Trait for Foo {
const CONST$0: () = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -218,7 +311,10 @@ impl Trait for Foo {
type Type$0 = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]]),
+ None,
+ None,
);
}
@@ -231,7 +327,10 @@ pub trait Foo {
fn method$0();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -240,7 +339,10 @@ pub trait Foo {
const CONST$0: ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -249,7 +351,10 @@ pub trait Foo {
type Type$0;
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]]),
+ None,
+ None,
);
}
@@ -260,7 +365,10 @@ fn external_docs_trait() {
//- /main.rs crate:foo
trait Trait$0 {}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]]),
+ None,
+ None,
)
}
@@ -273,7 +381,10 @@ pub mod foo {
pub mod ba$0r {}
}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]]),
+ None,
+ None,
)
}
@@ -294,7 +405,10 @@ fn foo() {
let bar: wrapper::It$0em;
}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]]),
+ None,
+ None,
)
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 418043d67..d6bbf2bf7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -76,15 +76,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
if let Some(item) = ast::Item::cast(node.clone()) {
if let Some(def) = sema.resolve_attr_macro_call(&item) {
break (
- def.name(db).to_string(),
+ def.name(db).display(db).to_string(),
expand_attr_macro_recur(&sema, &item)?,
SyntaxKind::MACRO_ITEMS,
);
}
}
if let Some(mac) = ast::MacroCall::cast(node) {
+ let mut name = mac.path()?.segment()?.name_ref()?.to_string();
+ name.push('!');
break (
- mac.path()?.segment()?.name_ref()?.to_string(),
+ name,
expand_macro_recur(&sema, &mac)?,
mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS),
);
@@ -149,9 +151,11 @@ fn _format(
_db: &RootDatabase,
_kind: SyntaxKind,
_file_id: FileId,
- _expansion: &str,
+ expansion: &str,
) -> Option<String> {
- None
+ // remove trailing spaces for test
+ use itertools::Itertools;
+ Some(expansion.lines().map(|x| x.trim_end()).join("\n"))
}
#[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))]
@@ -235,7 +239,7 @@ fn main() {
}
"#,
expect![[r#"
- bar
+ bar!
5i64 as _"#]],
);
}
@@ -252,7 +256,7 @@ fn main() {
}
"#,
expect![[r#"
- bar
+ bar!
for _ in 0..42{}"#]],
);
}
@@ -273,9 +277,8 @@ macro_rules! baz {
f$0oo!();
"#,
expect![[r#"
- foo
- fn b(){}
- "#]],
+ foo!
+ fn b(){}"#]],
);
}
@@ -294,7 +297,7 @@ macro_rules! foo {
f$0oo!();
"#,
expect![[r#"
- foo
+ foo!
fn some_thing() -> u32 {
let a = 0;
a+10
@@ -328,16 +331,16 @@ fn main() {
}
"#,
expect![[r#"
- match_ast
- {
- if let Some(it) = ast::TraitDef::cast(container.clone()){}
- else if let Some(it) = ast::ImplDef::cast(container.clone()){}
- else {
- {
- continue
- }
- }
- }"#]],
+ match_ast!
+ {
+ if let Some(it) = ast::TraitDef::cast(container.clone()){}
+ else if let Some(it) = ast::ImplDef::cast(container.clone()){}
+ else {
+ {
+ continue
+ }
+ }
+ }"#]],
);
}
@@ -358,7 +361,7 @@ fn main() {
}
"#,
expect![[r#"
- match_ast
+ match_ast!
{}"#]],
);
}
@@ -383,7 +386,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
{
macro_rules! bar {
() => {
@@ -411,7 +414,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
"#]],
);
}
@@ -433,7 +436,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
0"#]],
);
}
@@ -451,7 +454,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
);
}
@@ -469,8 +472,17 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >{}
- "#]],
+ impl < >core::clone::Clone for Foo< >where {
+ fn clone(&self) -> Self {
+ match self {
+ Foo{}
+ => Foo{}
+ ,
+
+ }
+ }
+
+ }"#]],
);
}
@@ -486,8 +498,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >{}
- "#]],
+ impl < >core::marker::Copy for Foo< >where{}"#]],
);
}
@@ -502,8 +513,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >{}
- "#]],
+ impl < >core::marker::Copy for Foo< >where{}"#]],
);
check(
r#"
@@ -514,8 +524,17 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >{}
- "#]],
+ impl < >core::clone::Clone for Foo< >where {
+ fn clone(&self) -> Self {
+ match self {
+ Foo{}
+ => Foo{}
+ ,
+
+ }
+ }
+
+ }"#]],
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 9f78c75e9..f90618222 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -39,7 +39,7 @@ fn try_extend_selection(
) -> Option<TextRange> {
let range = frange.range;
- let string_kinds = [COMMENT, STRING, BYTE_STRING];
+ let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING];
let list_kinds = [
RECORD_PAT_FIELD_LIST,
MATCH_ARM_LIST,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
new file mode 100644
index 000000000..46ee671de
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
@@ -0,0 +1,42 @@
+use ide_db::{
+ base_db::{CrateOrigin, FileId, SourceDatabase},
+ FxIndexSet, RootDatabase,
+};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct CrateInfo {
+ pub name: Option<String>,
+ pub version: Option<String>,
+ pub root_file_id: FileId,
+}
+
+// Feature: Show Dependency Tree
+//
+// Shows a view tree with all the dependencies of this project
+//
+// |===
+// | Editor | Panel Name
+//
+// | VS Code | **Rust Dependencies**
+// |===
+//
+// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
+pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
+ let crate_graph = db.crate_graph();
+ crate_graph
+ .iter()
+ .map(|crate_id| &crate_graph[crate_id])
+ .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. }))
+ .map(|data| crate_info(data))
+ .collect()
+}
+
+fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo {
+ let crate_name = crate_name(data);
+ let version = data.version.clone();
+ CrateInfo { name: crate_name, version, root_file_id: data.root_file_id }
+}
+
+fn crate_name(data: &ide_db::base_db::CrateData) -> Option<String> {
+ data.display_name.as_ref().map(|it| it.canonical_name().to_owned())
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index a32ac3549..b27892472 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -219,7 +219,7 @@ mod tests {
}
#[test]
- fn test_nagative_trait_bound() {
+ fn test_negative_trait_bound() {
let txt = r#"impl !Unpin for Test {}"#;
check(
txt,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
index 2ea6f6a9a..2e5903c06 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
@@ -1,5 +1,4 @@
//! Utilities for creating `Analysis` instances for tests.
-use hir::db::DefDatabase;
use ide_db::base_db::fixture::ChangeFixture;
use test_utils::{extract_annotations, RangeOrOffset};
@@ -9,7 +8,7 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
(host.analysis(), change_fixture.files[0])
}
@@ -18,7 +17,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -29,7 +28,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let range = range_or_offset.expect_range();
@@ -40,7 +39,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
(host.analysis(), file_id, range_or_offset)
@@ -50,7 +49,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -67,11 +66,11 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
(host.analysis(), FilePosition { file_id, offset }, annotations)
}
-/// Creates analysis from a multi-file fixture with annonations without $0
+/// Creates analysis from a multi-file fixture with annotations without $0
pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let annotations = change_fixture
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index cf0819a25..4e641357e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -113,6 +113,7 @@ fn try_lookup_include_path(
file_id,
full_range: TextRange::new(0.into(), size),
name: path.into(),
+ alias: None,
focus_range: None,
kind: None,
container_name: None,
@@ -833,8 +834,7 @@ fn test() {
#[rustc_builtin_macro]
macro_rules! include {}
- include!("foo.rs");
-//^^^^^^^^^^^^^^^^^^^
+include!("foo.rs");
fn f() {
foo$0();
@@ -846,6 +846,33 @@ mod confuse_index {
//- /foo.rs
fn foo() {}
+ //^^^
+ "#,
+ );
+ }
+
+ #[test]
+ fn goto_through_included_file_struct_with_doc_comment() {
+ check(
+ r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include {}
+
+include!("foo.rs");
+
+fn f() {
+ let x = Foo$0;
+}
+
+mod confuse_index {
+ pub struct Foo;
+}
+
+//- /foo.rs
+/// This is a doc comment
+pub struct Foo;
+ //^^^
"#,
);
}
@@ -1471,6 +1498,29 @@ fn f() {
);
}
#[test]
+ fn method_call_inside_block() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self);
+}
+
+fn outer() {
+ struct Stwuct;
+
+ impl Twait for Stwuct {
+ fn a(&self){}
+ //^
+ }
+ fn f() {
+ let s = Stwuct;
+ s.a$0();
+ }
+}
+ "#,
+ );
+ }
+ #[test]
fn path_call() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 6d2d0bd63..6048990f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// |===
// | Editor | Action Name
//
-// | VS Code | **Go to Type Definition*
+// | VS Code | **Go to Type Definition**
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
@@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition(
};
let range = token.text_range();
sema.descend_into_macros(token)
- .iter()
+ .into_iter()
.filter_map(|token| {
- let ty = sema.token_ancestors_with_macros(token.clone()).find_map(|node| {
- let ty = match_ast! {
- match node {
- ast::Expr(it) => sema.type_of_expr(&it)?.original,
- ast::Pat(it) => sema.type_of_pat(&it)?.original,
- ast::SelfParam(it) => sema.type_of_self(&it)?,
- ast::Type(it) => sema.resolve_type(&it)?,
- ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
- // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
- ast::NameRef(it) => {
- if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
- let (_, _, ty) = sema.resolve_record_field(&record_field)?;
- ty
- } else {
- let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
- sema.resolve_record_pat_field(&record_field)?.1
- }
- },
- _ => return None,
- }
- };
+ let ty = sema
+ .token_ancestors_with_macros(token)
+ // When `token` is within a macro call, we can't determine its type. Don't continue
+ // this traversal because otherwise we'll end up returning the type of *that* macro
+ // call, which is not what we want in general.
+ //
+ // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test
+ // if the current node is a `TokenTree`.
+ .take_while(|node| !ast::TokenTree::can_cast(node.kind()))
+ .find_map(|node| {
+ let ty = match_ast! {
+ match node {
+ ast::Expr(it) => sema.type_of_expr(&it)?.original,
+ ast::Pat(it) => sema.type_of_pat(&it)?.original,
+ ast::SelfParam(it) => sema.type_of_self(&it)?,
+ ast::Type(it) => sema.resolve_type(&it)?,
+ ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()),
+ // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
+ ast::NameRef(it) => {
+ if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
+ let (_, _, ty) = sema.resolve_record_field(&record_field)?;
+ ty
+ } else {
+ let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
+ sema.resolve_record_pat_field(&record_field)?.1
+ }
+ },
+ _ => return None,
+ }
+ };
- Some(ty)
- });
+ Some(ty)
+ });
ty
})
.for_each(|ty| {
@@ -94,7 +103,7 @@ mod tests {
fn check(ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
- assert_ne!(navs.len(), 0);
+ assert!(!navs.is_empty(), "navigation is empty");
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
let navs = navs
@@ -104,7 +113,7 @@ mod tests {
.collect::<Vec<_>>();
let expected = expected
.into_iter()
- .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
+ .map(|(file_range, _)| file_range)
.sorted_by_key(cmp)
.collect::<Vec<_>>();
assert_eq!(expected, navs);
@@ -199,6 +208,32 @@ id! {
}
#[test]
+ fn dont_collect_type_from_token_in_macro_call() {
+ check(
+ r#"
+struct DontCollectMe;
+struct S;
+ //^
+
+macro_rules! inner {
+ ($t:tt) => { DontCollectMe }
+}
+macro_rules! m {
+ ($t:ident) => {
+ match $t {
+ _ => inner!($t);
+ }
+ }
+}
+
+fn test() {
+ m!($0S);
+}
+"#,
+ );
+ }
+
+ #[test]
fn goto_type_definition_for_param() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index d88ffd25c..7e545491f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -1,10 +1,12 @@
use hir::Semantics;
use ide_db::{
- base_db::{FileId, FilePosition},
+ base_db::{FileId, FilePosition, FileRange},
defs::{Definition, IdentClass},
helpers::pick_best_token,
search::{FileReference, ReferenceCategory, SearchScope},
- syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr},
+ syntax_helpers::node_ext::{
+ for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr,
+ },
FxHashSet, RootDatabase,
};
use syntax::{
@@ -30,6 +32,7 @@ pub struct HighlightRelatedConfig {
pub references: bool,
pub exit_points: bool,
pub break_points: bool,
+ pub closure_captures: bool,
pub yield_points: bool,
}
@@ -38,11 +41,13 @@ pub struct HighlightRelatedConfig {
// Highlights constructs related to the thing under the cursor:
//
// . if on an identifier, highlights all references to that identifier in the current file
+// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
// . if on an `async` or `await token, highlights all yield points for that async context
// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
+// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
//
-// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor.
+// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
pub(crate) fn highlight_related(
sema: &Semantics<'_, RootDatabase>,
config: HighlightRelatedConfig,
@@ -53,11 +58,13 @@ pub(crate) fn highlight_related(
let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
- T![->] => 3,
- kind if kind.is_keyword() => 2,
- IDENT | INT_NUMBER => 1,
+ T![->] => 4,
+ kind if kind.is_keyword() => 3,
+ IDENT | INT_NUMBER => 2,
+ T![|] => 1,
_ => 0,
})?;
+ // most if not all of these should be re-implemented with information seeded from hir
match token.kind() {
T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => {
highlight_exit_points(sema, token)
@@ -70,18 +77,64 @@ pub(crate) fn highlight_related(
T![break] | T![loop] | T![while] | T![continue] if config.break_points => {
highlight_break_points(token)
}
+ T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
+ T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
_ if config.references => highlight_references(sema, &syntax, token, file_id),
_ => None,
}
}
+fn highlight_closure_captures(
+ sema: &Semantics<'_, RootDatabase>,
+ token: SyntaxToken,
+ file_id: FileId,
+) -> Option<Vec<HighlightedRange>> {
+ let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?;
+ let search_range = closure.body()?.syntax().text_range();
+ let ty = &sema.type_of_expr(&closure.into())?.original;
+ let c = ty.as_closure()?;
+ Some(
+ c.captured_items(sema.db)
+ .into_iter()
+ .map(|capture| capture.local())
+ .flat_map(|local| {
+ let usages = Definition::Local(local)
+ .usages(sema)
+ .set_scope(Some(SearchScope::file_range(FileRange {
+ file_id,
+ range: search_range,
+ })))
+ .include_self_refs()
+ .all()
+ .references
+ .remove(&file_id)
+ .into_iter()
+ .flatten()
+ .map(|FileReference { category, range, .. }| HighlightedRange {
+ range,
+ category,
+ });
+ let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+ local
+ .sources(sema.db)
+ .into_iter()
+ .map(|x| x.to_nav(sema.db))
+ .filter(|decl| decl.file_id == file_id)
+ .filter_map(|decl| decl.focus_range)
+ .map(move |range| HighlightedRange { range, category })
+ .chain(usages)
+ })
+ .collect(),
+ )
+}
+
fn highlight_references(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
token: SyntaxToken,
file_id: FileId,
) -> Option<Vec<HighlightedRange>> {
- let defs = find_defs(sema, token);
+ let defs = find_defs(sema, token.clone());
let usages = defs
.iter()
.filter_map(|&d| {
@@ -93,12 +146,62 @@ fn highlight_references(
.remove(&file_id)
})
.flatten()
- .map(|FileReference { category: access, range, .. }| HighlightedRange {
- range,
- category: access,
- });
+ .map(|FileReference { category, range, .. }| HighlightedRange { range, category });
let mut res = FxHashSet::default();
for &def in &defs {
+ // highlight trait usages
+ if let Definition::Trait(t) = def {
+ let trait_item_use_scope = (|| {
+ let name_ref = token.parent().and_then(ast::NameRef::cast)?;
+ let path = full_path_of_name_ref(&name_ref)?;
+ let parent = path.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::UseTree(it) => it.syntax().ancestors().find(|it| {
+ ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind())
+ }),
+ ast::PathType(it) => it
+ .syntax()
+ .ancestors()
+ .nth(2)
+ .and_then(ast::TypeBoundList::cast)?
+ .syntax()
+ .parent()
+ .filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))?
+ .ancestors()
+ .find(|it| {
+ ast::Item::can_cast(it.kind())
+ }),
+ _ => None,
+ }
+ }
+ })();
+ if let Some(trait_item_use_scope) = trait_item_use_scope {
+ res.extend(
+ t.items_with_supertraits(sema.db)
+ .into_iter()
+ .filter_map(|item| {
+ Definition::from(item)
+ .usages(sema)
+ .set_scope(Some(SearchScope::file_range(FileRange {
+ file_id,
+ range: trait_item_use_scope.text_range(),
+ })))
+ .include_self_refs()
+ .all()
+ .references
+ .remove(&file_id)
+ })
+ .flatten()
+ .map(|FileReference { category, range, .. }| HighlightedRange {
+ range,
+ category,
+ }),
+ );
+ }
+ }
+
+ // highlight the defs themselves
match def {
Definition::Local(local) => {
let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
@@ -148,9 +251,16 @@ fn highlight_exit_points(
) -> Option<Vec<HighlightedRange>> {
fn hl(
sema: &Semantics<'_, RootDatabase>,
+ def_ranges: [Option<TextRange>; 2],
body: Option<ast::Expr>,
) -> Option<Vec<HighlightedRange>> {
let mut highlights = Vec::new();
+ highlights.extend(
+ def_ranges
+ .into_iter()
+ .flatten()
+ .map(|range| HighlightedRange { category: None, range }),
+ );
let body = body?;
walk_expr(&body, &mut |expr| match expr {
ast::Expr::ReturnExpr(expr) => {
@@ -194,10 +304,21 @@ fn highlight_exit_points(
for anc in token.parent_ancestors() {
return match_ast! {
match anc {
- ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
- ast::ClosureExpr(closure) => hl(sema, closure.body()),
+ ast::Fn(fn_) => hl(sema, [fn_.fn_token().map(|it| it.text_range()), None], fn_.body().map(ast::Expr::BlockExpr)),
+ ast::ClosureExpr(closure) => hl(
+ sema,
+ closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]),
+ closure.body()
+ ),
ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
- hl(sema, Some(block_expr.into()))
+ hl(
+ sema,
+ [block_expr.modifier().and_then(|modifier| match modifier {
+ ast::BlockModifier::Async(t) | ast::BlockModifier::Try(t) | ast::BlockModifier::Const(t) => Some(t.text_range()),
+ _ => None,
+ }), None],
+ Some(block_expr.into())
+ )
} else {
continue;
},
@@ -352,16 +473,17 @@ mod tests {
use super::*;
+ const ENABLED_CONFIG: HighlightRelatedConfig = HighlightRelatedConfig {
+ break_points: true,
+ exit_points: true,
+ references: true,
+ closure_captures: true,
+ yield_points: true,
+ };
+
#[track_caller]
fn check(ra_fixture: &str) {
- let config = HighlightRelatedConfig {
- break_points: true,
- exit_points: true,
- references: true,
- yield_points: true,
- };
-
- check_with_config(ra_fixture, config);
+ check_with_config(ra_fixture, ENABLED_CONFIG);
}
#[track_caller]
@@ -571,6 +693,29 @@ pub async$0 fn foo() {
}
#[test]
+ fn test_hl_let_else_yield_points() {
+ check(
+ r#"
+pub async fn foo() {
+ // ^^^^^
+ let x = foo()
+ .await$0
+ // ^^^^^
+ .await;
+ // ^^^^^
+ || { 0.await };
+ let Some(_) = None else {
+ foo().await
+ // ^^^^^
+ };
+ (async { 0.await }).await
+ // ^^^^^
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_hl_yield_nested_fn() {
check(
r#"
@@ -610,7 +755,8 @@ async fn foo() {
fn test_hl_exit_points() {
check(
r#"
-fn foo() -> u32 {
+ fn foo() -> u32 {
+//^^
if true {
return$0 0;
// ^^^^^^
@@ -629,7 +775,8 @@ fn foo() -> u32 {
fn test_hl_exit_points2() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
if true {
return 0;
// ^^^^^^
@@ -648,7 +795,8 @@ fn foo() ->$0 u32 {
fn test_hl_exit_points3() {
check(
r#"
-fn$0 foo() -> u32 {
+ fn$0 foo() -> u32 {
+//^^
if true {
return 0;
// ^^^^^^
@@ -664,6 +812,26 @@ fn$0 foo() -> u32 {
}
#[test]
+ fn test_hl_let_else_exit_points() {
+ check(
+ r#"
+ fn$0 foo() -> u32 {
+//^^
+ let Some(bar) = None else {
+ return 0;
+ // ^^^^^^
+ };
+
+ 0?;
+ // ^
+ 0xDEAD_BEEF
+ // ^^^^^^^^^^^
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_hl_prefer_ref_over_tail_exit() {
check(
r#"
@@ -694,7 +862,8 @@ macro_rules! never {
() => { never() }
}
fn never() -> ! { loop {} }
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
never();
// ^^^^^^^
never!();
@@ -714,7 +883,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
if true {
unsafe {
return 5;
@@ -755,7 +925,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points_labeled_block() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
'foo: {
break 'foo 0;
// ^^^^^
@@ -776,7 +947,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points_loops() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
'foo: while { return 0; true } {
// ^^^^^^
break 'foo 0;
@@ -1086,12 +1258,7 @@ fn function(field: u32) {
#[test]
fn test_hl_disabled_ref_local() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1106,12 +1273,7 @@ fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_break() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1146,12 +1308,7 @@ fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_yield() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1182,12 +1339,7 @@ async fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_exit() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1207,7 +1359,8 @@ fn foo() -> i32 {
check_with_config(
r#"
-fn foo() ->$0 i32 {
+ fn foo() ->$0 i32 {
+//^^
let x = 5;
let y = x * 2;
@@ -1225,12 +1378,7 @@ fn foo() ->$0 i32 {
#[test]
fn test_hl_disabled_break() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: false,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { break_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1246,12 +1394,7 @@ fn foo() {
#[test]
fn test_hl_disabled_yield() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: true,
- exit_points: true,
- yield_points: false,
- };
+ let config = HighlightRelatedConfig { yield_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1265,12 +1408,7 @@ async$0 fn foo() {
#[test]
fn test_hl_disabled_exit() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: true,
- exit_points: false,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { exit_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1414,4 +1552,73 @@ impl Trait for () {
"#,
);
}
+
+ #[test]
+ fn test_closure_capture_pipe() {
+ check(
+ r#"
+fn f() {
+ let x = 1;
+ // ^
+ let c = $0|y| x + y;
+ // ^ read
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_closure_capture_move() {
+ check(
+ r#"
+fn f() {
+ let x = 1;
+ // ^
+ let c = move$0 |y| x + y;
+ // ^ read
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_trait_highlights_assoc_item_uses() {
+ check(
+ r#"
+trait Foo {
+ //^^^
+ type T;
+ const C: usize;
+ fn f() {}
+ fn m(&self) {}
+}
+impl Foo for i32 {
+ //^^^
+ type T = i32;
+ const C: usize = 0;
+ fn f() {}
+ fn m(&self) {}
+}
+fn f<T: Foo$0>(t: T) {
+ //^^^
+ let _: T::T;
+ //^
+ t.m();
+ //^
+ T::C;
+ //^
+ T::f();
+ //^
+}
+
+fn f2<T: Foo>(t: T) {
+ //^^^
+ let _: T::T;
+ t.m();
+ T::C;
+ T::f();
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 64b2221bd..5ef6ac948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
use std::iter;
use either::Either;
-use hir::{HasSource, Semantics};
+use hir::{db::DefDatabase, HasSource, LangItem, Semantics};
use ide_db::{
base_db::FileRange,
defs::{Definition, IdentClass, OperatorClass},
@@ -27,10 +27,25 @@ use crate::{
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
pub links_in_hover: bool,
+ pub memory_layout: Option<MemoryLayoutHoverConfig>,
pub documentation: bool,
pub keywords: bool,
pub format: HoverDocFormat,
- pub interpret_tests: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct MemoryLayoutHoverConfig {
+ pub size: Option<MemoryLayoutHoverRenderKind>,
+ pub offset: Option<MemoryLayoutHoverRenderKind>,
+ pub alignment: Option<MemoryLayoutHoverRenderKind>,
+ pub niches: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum MemoryLayoutHoverRenderKind {
+ Decimal,
+ Hexadecimal,
+ Both,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -56,7 +71,7 @@ impl HoverAction {
mod_path: render::path(
db,
it.module(db)?,
- it.name(db).map(|name| name.to_string()),
+ it.name(db).map(|name| name.display(db).to_string()),
),
nav: it.try_to_nav(db)?,
})
@@ -119,8 +134,8 @@ fn hover_simple(
| T![crate]
| T![Self]
| T![_] => 4,
- // index and prefix ops
- T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+ // index and prefix ops and closure pipe
+ T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
kind if kind.is_keyword() => 2,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
@@ -219,6 +234,16 @@ fn hover_simple(
};
render::type_info_of(sema, config, &Either::Left(call_expr))
})
+ })
+ // try closure
+ .or_else(|| {
+ descended().find_map(|token| {
+ if token.kind() != T![|] {
+ return None;
+ }
+ let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
+ render::closure_expr(sema, config, c)
+ })
});
result.map(|mut res: HoverResult| {
@@ -344,7 +369,14 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverA
};
if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
- it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
+ let krate = it.module(db).krate();
+ let sized_trait =
+ db.lang_item(krate.into(), LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
+
+ it.trait_bounds(db)
+ .into_iter()
+ .filter(|&it| Some(it.into()) != sized_trait)
+ .for_each(|it| push_new_def(it.into()));
} else {
let ty = match def {
Definition::Local(it) => it.ty(db),
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index da725ce50..136214641 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,8 +3,8 @@ use std::fmt::Display;
use either::Either;
use hir::{
- db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
- MirEvalError, Semantics, TypeInfo,
+ Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout,
+ LayoutError, Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
@@ -27,7 +27,8 @@ use syntax::{
use crate::{
doc_links::{remove_links, rewrite_links},
hover::walk_and_push_ty,
- HoverAction, HoverConfig, HoverResult, Markup,
+ HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
+ MemoryLayoutHoverRenderKind,
};
pub(super) fn type_info_of(
@@ -35,11 +36,20 @@ pub(super) fn type_info_of(
_config: &HoverConfig,
expr_or_pat: &Either<ast::Expr, ast::Pat>,
) -> Option<HoverResult> {
- let TypeInfo { original, adjusted } = match expr_or_pat {
+ let ty_info = match expr_or_pat {
Either::Left(expr) => sema.type_of_expr(expr)?,
Either::Right(pat) => sema.type_of_pat(pat)?,
};
- type_info(sema, _config, original, adjusted)
+ type_info(sema, _config, ty_info)
+}
+
+pub(super) fn closure_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ c: ast::ClosureExpr,
+) -> Option<HoverResult> {
+ let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
+ closure_ty(sema, config, &TypeInfo { original, adjusted: None })
}
pub(super) fn try_expr(
@@ -361,7 +371,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
_ => None,
}
- .map(|name| name.to_string())
+ .map(|name| name.display(db).to_string())
}
pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
@@ -371,7 +381,7 @@ pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<Str
.path_to_root(db)
.into_iter()
.rev()
- .flat_map(|it| it.name(db).map(|name| name.to_string()));
+ .flat_map(|it| it.name(db).map(|name| name.display(db).to_string()));
crate_name.into_iter().chain(module_path).chain(item_name).join("::")
}
@@ -384,53 +394,46 @@ pub(super) fn definition(
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
Definition::Macro(it) => label_and_docs(db, it),
- Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
- let var_def = it.parent_def(db);
- let id = it.index();
- let layout = it.layout(db).ok()?;
- let offset = match var_def {
- hir::VariantDef::Struct(s) => Adt::from(s)
- .layout(db)
- .ok()
- .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())),
- _ => None,
- };
- Some(format!(
- "size = {}, align = {}{}",
- layout.size.bytes(),
- layout.align.abi.bytes(),
- offset.as_deref().unwrap_or_default()
- ))
- }),
- Definition::Module(it) => label_and_docs(db, it),
- Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
- if !config.interpret_tests {
- return None;
- }
- match it.eval(db) {
- Ok(()) => Some("pass".into()),
- Err(MirEvalError::Panic) => Some("fail".into()),
- Err(MirEvalError::MirLowerError(f, e)) => {
- let name = &db.function_data(f).name;
- Some(format!("error: fail to lower {name} due {e:?}"))
+ Definition::Field(it) => label_and_layout_info_and_docs(
+ db,
+ it,
+ config,
+ |&it| it.layout(db),
+ |_| {
+ let var_def = it.parent_def(db);
+ let id = it.index();
+ match var_def {
+ hir::VariantDef::Struct(s) => {
+ Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id))
+ }
+ _ => None,
}
- Err(e) => Some(format!("error: {e:?}")),
- }
- }),
- Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
- let layout = it.layout(db).ok()?;
- Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
- }),
- Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
- if !it.parent_enum(db).is_data_carrying(db) {
- match it.eval(db) {
- Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
- Err(_) => it.value(db).map(|x| format!("{x:?}")),
+ },
+ ),
+ Definition::Module(it) => label_and_docs(db, it),
+ Definition::Function(it) => label_and_docs(db, it),
+ Definition::Adt(it) => {
+ label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None)
+ }
+ Definition::Variant(it) => label_value_and_layout_info_and_docs(
+ db,
+ it,
+ config,
+ |&it| {
+ if !it.parent_enum(db).is_data_carrying(db) {
+ match it.eval(db) {
+ Ok(x) => {
+ Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") })
+ }
+ Err(_) => it.value(db).map(|x| format!("{x:?}")),
+ }
+ } else {
+ None
}
- } else {
- None
- }
- }),
+ },
+ |it| it.layout(db),
+ |layout| layout.enum_tag_size(),
+ ),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.render_eval(db);
match body {
@@ -455,22 +458,26 @@ pub(super) fn definition(
}),
Definition::Trait(it) => label_and_docs(db, it),
Definition::TraitAlias(it) => label_and_docs(db, it),
- Definition::TypeAlias(it) => label_and_docs(db, it),
+ Definition::TypeAlias(it) => {
+ label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None)
+ }
Definition::BuiltinType(it) => {
return famous_defs
.and_then(|fd| builtin(fd, it))
- .or_else(|| Some(Markup::fenced_block(&it.name())))
+ .or_else(|| Some(Markup::fenced_block(&it.name().display(db))))
}
- Definition::Local(it) => return local(db, it),
+ Definition::Local(it) => return local(db, it, config),
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
}
Definition::GenericParam(it) => label_and_docs(db, it),
- Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
+ Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
// FIXME: We should be able to show more info about these
Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
- Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
+ Definition::DeriveHelper(it) => {
+ (format!("derive_helper {}", it.name(db).display(db)), None)
+ }
};
let docs = docs
@@ -490,10 +497,13 @@ pub(super) fn definition(
fn type_info(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
- original: hir::Type,
- adjusted: Option<hir::Type>,
+ config: &HoverConfig,
+ ty: TypeInfo,
) -> Option<HoverResult> {
+ if let Some(res) = closure_ty(sema, config, &ty) {
+ return Some(res);
+ };
+ let TypeInfo { original, adjusted } = ty;
let mut res = HoverResult::default();
let mut targets: Vec<hir::ModuleDef> = Vec::new();
let mut push_new_def = |item: hir::ModuleDef| {
@@ -523,6 +533,67 @@ fn type_info(
Some(res)
}
+fn closure_ty(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ TypeInfo { original, adjusted }: &TypeInfo,
+) -> Option<HoverResult> {
+ let c = original.as_closure()?;
+ let mut captures_rendered = c.captured_items(sema.db)
+ .into_iter()
+ .map(|it| {
+ let borrow_kind = match it.kind() {
+ CaptureKind::SharedRef => "immutable borrow",
+ CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
+ CaptureKind::MutableRef => "mutable borrow",
+ CaptureKind::Move => "move",
+ };
+ format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
+ })
+ .join("\n");
+ if captures_rendered.trim().is_empty() {
+ captures_rendered = "This closure captures nothing".to_string();
+ }
+ let mut targets: Vec<hir::ModuleDef> = Vec::new();
+ let mut push_new_def = |item: hir::ModuleDef| {
+ if !targets.contains(&item) {
+ targets.push(item);
+ }
+ };
+ walk_and_push_ty(sema.db, original, &mut push_new_def);
+ c.capture_types(sema.db).into_iter().for_each(|ty| {
+ walk_and_push_ty(sema.db, &ty, &mut push_new_def);
+ });
+
+ let adjusted = if let Some(adjusted_ty) = adjusted {
+ walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+ format!(
+ "\nCoerced to: {}",
+ adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn)
+ )
+ } else {
+ String::new()
+ };
+ let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),);
+
+ if let Some(layout) =
+ render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
+ {
+ format_to!(markup, "{layout}");
+ }
+ format_to!(
+ markup,
+ "\n{}\n```{adjusted}\n\n## Captures\n{}",
+ c.display_with_impl(sema.db),
+ captures_rendered,
+ );
+
+ let mut res = HoverResult::default();
+ res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+ res.markup = markup.into();
+ Some(res)
+}
+
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
let name = attr.name(db);
let desc = format!("#[{name}]");
@@ -553,21 +624,59 @@ where
(label, docs)
}
-fn label_and_layout_info_and_docs<D, E, V>(
+fn label_and_layout_info_and_docs<D, E, E2>(
+ db: &RootDatabase,
+ def: D,
+ config: &HoverConfig,
+ layout_extractor: E,
+ layout_offset_extractor: E2,
+) -> (String, Option<hir::Documentation>)
+where
+ D: HasAttrs + HirDisplay,
+ E: Fn(&D) -> Result<Layout, LayoutError>,
+ E2: Fn(&Layout) -> Option<u64>,
+{
+ let mut label = def.display(db).to_string();
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || layout_extractor(&def),
+ layout_offset_extractor,
+ |_| None,
+ ) {
+ format_to!(label, "{layout}");
+ }
+ let docs = def.attrs(db).docs();
+ (label, docs)
+}
+
+fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
db: &RootDatabase,
def: D,
+ config: &HoverConfig,
value_extractor: E,
+ layout_extractor: E2,
+ layout_tag_extractor: E3,
) -> (String, Option<hir::Documentation>)
where
D: HasAttrs + HirDisplay,
E: Fn(&D) -> Option<V>,
+ E2: Fn(&D) -> Result<Layout, LayoutError>,
+ E3: Fn(&Layout) -> Option<usize>,
V: Display,
{
- let label = if let Some(value) = value_extractor(&def) {
- format!("{} // {value}", def.display(db))
- } else {
- def.display(db).to_string()
+ let value = value_extractor(&def);
+ let mut label = match value {
+ Some(value) => format!("{} = {value}", def.display(db)),
+ None => def.display(db).to_string(),
};
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || layout_extractor(&def),
+ |_| None,
+ layout_tag_extractor,
+ ) {
+ format_to!(label, "{layout}");
+ }
let docs = def.attrs(db).docs();
(label, docs)
}
@@ -616,26 +725,26 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option<Markup> {
// std exposes prim_{} modules with docstrings on the root to document the builtins
- let primitive_mod = format!("prim_{}", builtin.name());
+ let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db));
let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
- markup(Some(docs.into()), builtin.name().to_string(), None)
+ markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None)
}
fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
let db = famous_defs.0.db;
let std_crate = famous_defs.std()?;
let std_root_module = std_crate.root_module(db);
- std_root_module
- .children(db)
- .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
+ std_root_module.children(db).find(|module| {
+ module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+ })
}
-fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
+fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
let ty = it.ty(db);
let ty = ty.display_truncated(db, None);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
- let desc = match it.primary_source(db).into_ident_pat() {
+ let mut desc = match it.primary_source(db).into_ident_pat() {
Some(ident) => {
let name = it.name(db);
let let_kw = if ident
@@ -647,13 +756,91 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
} else {
""
};
- format!("{let_kw}{is_mut}{name}: {ty}")
+ format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
}
None => format!("{is_mut}self: {ty}"),
};
+ if let Some(layout) =
+ render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
+ {
+ format_to!(desc, "{layout}");
+ }
markup(None, desc, None)
}
+fn render_memory_layout(
+ config: Option<MemoryLayoutHoverConfig>,
+ layout: impl FnOnce() -> Result<Layout, LayoutError>,
+ offset: impl FnOnce(&Layout) -> Option<u64>,
+ tag: impl FnOnce(&Layout) -> Option<usize>,
+) -> Option<String> {
+ // field
+
+ let config = config?;
+ let layout = layout().ok()?;
+
+ let mut label = String::from(" // ");
+
+ if let Some(render) = config.size {
+ let size = match tag(&layout) {
+ Some(tag) => layout.size() as usize - tag,
+ None => layout.size() as usize,
+ };
+ format_to!(label, "size = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"),
+ MemoryLayoutHoverRenderKind::Both if size >= 10 => {
+ format_to!(label, "{size} ({size:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"),
+ }
+ format_to!(label, ", ");
+ }
+
+ if let Some(render) = config.alignment {
+ let align = layout.align();
+ format_to!(label, "align = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",),
+ MemoryLayoutHoverRenderKind::Both if align >= 10 => {
+ format_to!(label, "{align} ({align:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{align}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+
+ if let Some(render) = config.offset {
+ if let Some(offset) = offset(&layout) {
+ format_to!(label, "offset = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"),
+ MemoryLayoutHoverRenderKind::Both if offset >= 10 => {
+ format_to!(label, "{offset} ({offset:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{offset}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+ }
+
+ if config.niches {
+ if let Some(niches) = layout.niches() {
+ format_to!(label, "niches = {niches}, ");
+ }
+ }
+ label.pop(); // ' '
+ label.pop(); // ','
+ Some(label)
+}
+
struct KeywordHint {
description: String,
keyword_mod: String,
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 57bf0f9ad..f75ebfa12 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -2,14 +2,21 @@ use expect_test::{expect, Expect};
use ide_db::base_db::{FileLoader, FileRange};
use syntax::TextRange;
-use crate::{fixture, HoverConfig, HoverDocFormat};
+use crate::{
+ fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+};
const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
links_in_hover: false,
+ memory_layout: Some(MemoryLayoutHoverConfig {
+ size: Some(MemoryLayoutHoverRenderKind::Both),
+ offset: Some(MemoryLayoutHoverRenderKind::Both),
+ alignment: Some(MemoryLayoutHoverRenderKind::Both),
+ niches: true,
+ }),
documentation: true,
format: HoverDocFormat::Markdown,
keywords: true,
- interpret_tests: false,
};
fn check_hover_no_result(ra_fixture: &str) {
@@ -58,6 +65,23 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
+fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
+ let (analysis, position) = fixture::position(ra_fixture);
+ let hover = analysis
+ .hover(
+ &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG },
+ FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+ )
+ .unwrap()
+ .unwrap();
+
+ let content = analysis.db.file_text(position.file_id);
+ let hovered_element = &content[hover.range];
+
+ let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
+ expect.assert_eq(&actual)
+}
+
fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
@@ -97,6 +121,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
expect.assert_eq(hover.info.markup.as_str())
}
+fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+ let (analysis, range) = fixture::range(ra_fixture);
+ let hover = analysis
+ .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
+ .unwrap()
+ .unwrap();
+ expect.assert_debug_eq(&hover.info.actions);
+}
+
fn check_hover_range_no_results(ra_fixture: &str) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
@@ -124,7 +157,7 @@ fn foo() {
*local*
```rust
- let local: i32
+ let local: i32 // size = 4, align = 4
```
"#]],
);
@@ -199,6 +232,181 @@ fn main() {
}
#[test]
+fn hover_closure() {
+ check(
+ r#"
+//- minicore: copy
+fn main() {
+ let x = 2;
+ let y = $0|z| x + z;
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ * `x` by immutable borrow
+ "#]],
+ );
+
+ check(
+ r#"
+//- minicore: copy
+fn foo(x: impl Fn(i32) -> i32) {
+
+}
+fn main() {
+ foo($0|x: i32| x)
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 0, align = 1
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ This closure captures nothing
+ "#]],
+ );
+
+ check(
+ r#"
+//- minicore: copy
+
+struct Z { f: i32 }
+
+struct Y(&'static mut Z)
+
+struct X {
+ f1: Y,
+ f2: (Y, Y),
+}
+
+fn main() {
+ let x: X;
+ let y = $0|| {
+ x.f1;
+ &mut x.f2.0 .0.f;
+ };
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 16 (0x10), align = 8, niches = 1
+ impl FnOnce()
+ ```
+
+ ## Captures
+ * `x.f1` by move
+ * `(*x.f2.0.0).f` by mutable borrow
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: copy, option
+
+fn do_char(c: char) {}
+
+fn main() {
+ let x = None;
+ let y = |$0| {
+ match x {
+ Some(c) => do_char(c),
+ None => x = None,
+ }
+ };
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl FnMut()
+ ```
+
+ ## Captures
+ * `x` by mutable borrow
+ "#]],
+ );
+}
+
+#[test]
+fn hover_ranged_closure() {
+ check_hover_range(
+ r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+ let x = &S;
+ let y = ($0|| {x; S2}$0).call();
+}
+"#,
+ expect![[r#"
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl FnOnce() -> S2
+ ```
+ Coerced to: &impl FnOnce() -> S2
+
+ ## Captures
+ * `x` by move"#]],
+ );
+ check_hover_range_actions(
+ r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+ let x = &S;
+ let y = ($0|| {x; S2}$0).call();
+}
+"#,
+ expect![[r#"
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::S2",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 10..20,
+ focus_range: 17..19,
+ name: "S2",
+ kind: Struct,
+ description: "struct S2",
+ },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::S",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..9,
+ focus_range: 7..8,
+ name: "S",
+ kind: Struct,
+ description: "struct S",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
fn hover_shows_long_type_of_an_expression() {
check(
r#"
@@ -222,12 +430,12 @@ fn main() {
}
"#,
expect![[r#"
- *iter*
+ *iter*
- ```rust
- let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
- ```
- "#]],
+ ```rust
+ let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> // size = 8, align = 4
+ ```
+ "#]],
);
}
@@ -604,12 +812,12 @@ fn main() {
let zz$0 = Test { t: 23u8, k: 33 };
}"#,
expect![[r#"
- *zz*
+ *zz*
- ```rust
- let zz: Test<i32>
- ```
- "#]],
+ ```rust
+ let zz: Test<i32> // size = 8, align = 4
+ ```
+ "#]],
);
check_hover_range(
r#"
@@ -655,12 +863,12 @@ use Option::Some;
fn main() { let b$0ar = Some(12); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- let bar: Option<i32>
- ```
- "#]],
+ ```rust
+ let bar: Option<i32> // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -724,12 +932,12 @@ fn hover_for_local_variable() {
check(
r#"fn func(foo: i32) { fo$0o; }"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -738,12 +946,12 @@ fn hover_for_local_variable_pat() {
check(
r#"fn func(fo$0o: i32) {}"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -752,12 +960,12 @@ fn hover_local_var_edge() {
check(
r#"fn func(foo: i32) { if true { $0foo; }; }"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -766,12 +974,12 @@ fn hover_for_param_edge() {
check(
r#"fn func($0foo: i32) {}"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -810,12 +1018,12 @@ impl Thing {
fn main() { let foo_$0test = Thing::new(); }
"#,
expect![[r#"
- *foo_test*
+ *foo_test*
- ```rust
- let foo_test: Thing
- ```
- "#]],
+ ```rust
+ let foo_test: Thing // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -970,12 +1178,12 @@ fn y() {
}
"#,
expect![[r#"
- *x*
+ *x*
- ```rust
- let x: i32
- ```
- "#]],
+ ```rust
+ let x: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -1100,12 +1308,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- bar: u32
- ```
- "#]],
+ ```rust
+ bar: u32 // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -1118,12 +1326,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- bar: u32
- ```
- "#]],
+ ```rust
+ bar: u32 // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -1320,16 +1528,16 @@ fn test_hover_function_pointer_show_identifiers() {
check(
r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type foo = fn(a: i32, b: i32) -> i32
- ```
- "#]],
+ ```rust
+ type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -1338,16 +1546,16 @@ fn test_hover_function_pointer_no_identifier() {
check(
r#"type foo$0 = fn(i32, _: i32) -> i32;"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type foo = fn(i32, i32) -> i32
- ```
- "#]],
+ ```rust
+ type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -1668,6 +1876,86 @@ pub fn fo$0o() {}
}
#[test]
+fn test_hover_layout_of_variant() {
+ check(
+ r#"enum Foo {
+ Va$0riant1(u8, u16),
+ Variant2(i32, u8, i64),
+ }"#,
+ expect![[r#"
+ *Variant1*
+
+ ```rust
+ test::Foo
+ ```
+
+ ```rust
+ Variant1(u8, u16) // size = 4, align = 2
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_layout_of_enum() {
+ check(
+ r#"enum $0Foo {
+ Variant1(u8, u16),
+ Variant2(i32, u8, i64),
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ enum Foo // size = 16 (0x10), align = 8, niches = 254
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_no_memory_layout() {
+ check_hover_no_memory_layout(
+ r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#,
+ expect![[r#"
+ *field_a*
+
+ ```rust
+ test::Foo
+ ```
+
+ ```rust
+ field_a: u8
+ ```
+ "#]],
+ );
+
+ check_hover_no_memory_layout(
+ r#"
+//- minicore: copy
+fn main() {
+ let x = 2;
+ let y = $0|z| x + z;
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0}
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ * `x` by immutable borrow
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_macro_generated_struct_fn_doc_comment() {
cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
@@ -2021,6 +2309,19 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
}
#[test]
+fn test_hover_generic_excludes_sized_go_to_action() {
+ check_actions(
+ r#"
+//- minicore: sized
+struct S<T$0>(T);
+ "#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_generic_struct_has_flattened_goto_type_actions() {
check_actions(
r#"
@@ -2079,52 +2380,53 @@ mod M {
fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
"#,
expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::A",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..14,
- focus_range: 7..8,
- name: "A",
- kind: Struct,
- description: "struct A",
- },
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::A",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..14,
+ focus_range: 7..8,
+ name: "A",
+ kind: Struct,
+ description: "struct A",
},
- HoverGotoTypeData {
- mod_path: "test::B",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 15..29,
- focus_range: 22..23,
- name: "B",
- kind: Struct,
- description: "struct B",
- },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::B",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 15..29,
+ focus_range: 22..23,
+ name: "B",
+ kind: Struct,
+ description: "struct B",
},
- HoverGotoTypeData {
- mod_path: "test::M::C",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 42..60,
- focus_range: 53..54,
- name: "C",
- kind: Struct,
- description: "pub struct C",
- },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::M::C",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 42..60,
+ focus_range: 53..54,
+ name: "C",
+ kind: Struct,
+ container_name: "M",
+ description: "pub struct C",
},
- ],
- ),
- ]
- "#]],
+ },
+ ],
+ ),
+ ]
+ "#]],
);
}
@@ -2453,6 +2755,7 @@ pub mod future {
focus_range: 60..66,
name: "Future",
kind: Trait,
+ container_name: "future",
description: "pub trait Future",
},
},
@@ -2908,7 +3211,7 @@ fn main() {
*f*
```rust
- f: &i32
+ f: &i32 // size = 8, align = 8, niches = 1
```
---
@@ -2958,7 +3261,7 @@ fn main() {
*value*
```rust
- let value: Const<1>
+ let value: Const<1> // size = 0, align = 1
```
"#]],
);
@@ -2978,7 +3281,7 @@ fn main() {
*value*
```rust
- let value: Const<0>
+ let value: Const<0> // size = 0, align = 1
```
"#]],
);
@@ -2998,7 +3301,7 @@ fn main() {
*value*
```rust
- let value: Const<-1>
+ let value: Const<-1> // size = 0, align = 1
```
"#]],
);
@@ -3018,7 +3321,7 @@ fn main() {
*value*
```rust
- let value: Const<true>
+ let value: Const<true> // size = 0, align = 1
```
"#]],
);
@@ -3038,7 +3341,7 @@ fn main() {
*value*
```rust
- let value: Const<'🦀'>
+ let value: Const<'🦀'> // size = 0, align = 1
```
"#]],
);
@@ -3054,12 +3357,12 @@ impl Foo {
}
"#,
expect![[r#"
- *self*
+ *self*
- ```rust
- self: &Foo
- ```
- "#]],
+ ```rust
+ self: &Foo // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -3074,12 +3377,12 @@ impl Foo {
}
"#,
expect![[r#"
- *self*
+ *self*
- ```rust
- self: Arc<Foo>
- ```
- "#]],
+ ```rust
+ self: Arc<Foo> // size = 0, align = 1
+ ```
+ "#]],
);
}
@@ -3115,7 +3418,7 @@ mod Foo$0 {
}
#[test]
-fn hover_doc_outer_inner_attribue() {
+fn hover_doc_outer_inner_attribute() {
check(
r#"
#[doc = "Be quick;"]
@@ -3146,7 +3449,7 @@ mod Foo$0 {
}
#[test]
-fn hover_doc_block_style_indentend() {
+fn hover_doc_block_style_indent_end() {
check(
r#"
/**
@@ -3455,16 +3758,16 @@ struct Foo<const LEN: usize>;
type Fo$0o2 = Foo<2>;
"#,
expect![[r#"
- *Foo2*
+ *Foo2*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type Foo2 = Foo<2>
- ```
- "#]],
+ ```rust
+ type Foo2 = Foo<2> // size = 0, align = 1
+ ```
+ "#]],
);
}
@@ -3504,7 +3807,7 @@ enum E {
```
```rust
- A = 8
+ A = 8 // size = 1, align = 1
```
---
@@ -3529,7 +3832,7 @@ enum E {
```
```rust
- A = 12 (0xC)
+ A = 12 (0xC) // size = 1, align = 1
```
---
@@ -3555,7 +3858,7 @@ enum E {
```
```rust
- B = 2
+ B = 2 // size = 1, align = 1
```
---
@@ -3581,7 +3884,7 @@ enum E {
```
```rust
- B = 5
+ B = 5 // size = 1, align = 1
```
---
@@ -4035,6 +4338,278 @@ const FOO$0: f64 = 1.0f64;
}
#[test]
+fn hover_const_eval_floating_point() {
+ check(
+ r#"
+extern "rust-intrinsic" {
+ pub fn expf64(x: f64) -> f64;
+}
+
+const FOO$0: f64 = expf64(1.2);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: f64 = 3.3201169227365472
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_enum() {
+ check(
+ r#"
+enum Enum {
+ V1,
+ V2,
+}
+
+const VX: Enum = Enum::V1;
+
+const FOO$0: Enum = VX;
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Enum = V1
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: option
+const FOO$0: Option<i32> = Some(2);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Option<i32> = Some(2)
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: option
+const FOO$0: Option<&i32> = Some(2).as_ref();
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Option<&i32> = Some(&2)
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_dyn_trait() {
+ check(
+ r#"
+//- minicore: fmt, coerce_unsized, builtin_impls
+use core::fmt::Debug;
+
+const FOO$0: &dyn Debug = &2i32;
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &dyn Debug = &2
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_slice() {
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32] = &[1, 2, 3 + 4];
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &[i32] = &[1, 2, 7]
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32; 5] = &[12; 5];
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &[i32; 5] = &[12, 12, 12, 12, 12]
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+const FOO$0: (&i32, &[i32], &i32) = {
+ let a: &[i32] = &[1, 2, 3];
+ (&a[0], a, &a[0])
+}
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1)
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+struct Tree(&[Tree]);
+
+const FOO$0: Tree = {
+ let x = &[Tree(&[]), Tree(&[Tree(&[])])];
+ Tree(&[Tree(x), Tree(x)])
+}
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])])
+ ```
+ "#]],
+ );
+ // FIXME: Show the data of unsized structs
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized, transmute
+#[repr(transparent)]
+struct S<T: ?Sized>(T);
+const FOO$0: &S<[u8]> = core::mem::transmute::<&[u8], _>(&[1, 2, 3]);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &S<[u8]> = &S
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_str() {
+ check(
+ r#"
+const FOO$0: &str = "foo";
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &str = "foo"
+ ```
+ "#]],
+ );
+ check(
+ r#"
+struct X {
+ a: &'static str,
+ b: &'static str,
+}
+const FOO$0: X = X {
+ a: "axiom",
+ b: "buy N large",
+};
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: X = X { a: "axiom", b: "buy N large" }
+ ```
+ "#]],
+ );
+ check(
+ r#"
+const FOO$0: (&str, &str) = {
+ let x = "foo";
+ (x, x)
+};
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: (&str, &str) = ("foo", "foo")
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn hover_const_eval_in_generic_trait() {
// Doesn't compile, but we shouldn't crash.
check(
@@ -4115,7 +4690,7 @@ fn foo(e: E) {
```
```rust
- A = 3
+ A = 3 // size = 0, align = 1
```
---
@@ -4137,9 +4712,9 @@ fn main() {
*tile4*
```rust
- let tile4: [u32; 8]
+ let tile4: [u32; 8] // size = 32 (0x20), align = 4
```
- "#]],
+ "#]],
);
}
@@ -4242,7 +4817,7 @@ fn foo() {
/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
mod move_keyword {}
"#,
- expect![[r##"
+ expect![[r#"
*move*
```rust
@@ -4251,11 +4826,11 @@ mod move_keyword {}
---
- [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
- [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
- [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads)
+ [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+ [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+ [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads)
<https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
- "##]],
+ "#]],
);
}
@@ -4288,7 +4863,7 @@ fn hover_builtin() {
check(
r#"
//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }
+const _: &str$0 = ""; }
//- /libstd.rs crate:std
/// Docs for prim_str
@@ -5009,7 +5584,7 @@ fn foo() {
fn hover_try_expr_res() {
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
fn foo() -> Result<(), FooError> {
@@ -5023,7 +5598,7 @@ fn foo() -> Result<(), FooError> {
);
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
struct BarError;
@@ -5044,6 +5619,7 @@ fn foo() -> Result<(), FooError> {
fn hover_try_expr() {
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5061,6 +5637,7 @@ fn foo() -> NotResult<(), Looooong> {
);
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5092,7 +5669,7 @@ fn foo() -> Option<()> {
"#,
expect![[r#"
```rust
- <Option<i32> as Try>::Output
+ i32
```"#]],
);
}
@@ -5312,7 +5889,7 @@ enum Enum {
```
```rust
- RecordV { field: u32 }
+ RecordV { field: u32 } // size = 4, align = 4
```
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index ac477339e..292591674 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -5,7 +5,8 @@ use std::{
use either::Either;
use hir::{
- known, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics,
+ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
+ ModuleDefId, Semantics,
};
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
use itertools::Itertools;
@@ -13,21 +14,23 @@ use smallvec::{smallvec, SmallVec};
use stdx::never;
use syntax::{
ast::{self, AstNode},
- match_ast, NodeOrToken, SyntaxNode, TextRange,
+ match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
};
+use text_edit::TextEdit;
use crate::{navigation_target::TryToNav, FileId};
-mod closing_brace;
-mod implicit_static;
-mod fn_lifetime_fn;
-mod closure_ret;
mod adjustment;
-mod chaining;
-mod param_name;
-mod binding_mode;
mod bind_pat;
+mod binding_mode;
+mod chaining;
+mod closing_brace;
+mod closure_ret;
+mod closure_captures;
mod discriminant;
+mod fn_lifetime_fn;
+mod implicit_static;
+mod param_name;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
@@ -40,11 +43,13 @@ pub struct InlayHintsConfig {
pub adjustment_hints_mode: AdjustmentHintsMode,
pub adjustment_hints_hide_outside_unsafe: bool,
pub closure_return_type_hints: ClosureReturnTypeHints,
+ pub closure_capture_hints: bool,
pub binding_mode_hints: bool,
pub lifetime_elision_hints: LifetimeElisionHints,
pub param_names_for_lifetime_elision_hints: bool,
pub hide_named_constructor_hints: bool,
pub hide_closure_initialization_hints: bool,
+ pub closure_style: ClosureStyle,
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
}
@@ -87,38 +92,61 @@ pub enum AdjustmentHintsMode {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InlayKind {
+ Adjustment,
BindingMode,
Chaining,
ClosingBrace,
- ClosureReturnType,
+ ClosureCapture,
+ Discriminant,
GenericParamList,
- Adjustment,
- AdjustmentPostfix,
Lifetime,
Parameter,
Type,
- Discriminant,
- OpeningParenthesis,
- ClosingParenthesis,
+}
+
+#[derive(Debug)]
+pub enum InlayHintPosition {
+ Before,
+ After,
}
#[derive(Debug)]
pub struct InlayHint {
/// The text range this inlay hint applies to.
pub range: TextRange,
- /// The kind of this inlay hint. This is used to determine side and padding of the hint for
- /// rendering purposes.
+ pub position: InlayHintPosition,
+ pub pad_left: bool,
+ pub pad_right: bool,
+ /// The kind of this inlay hint.
pub kind: InlayKind,
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
+ /// Text edit to apply when "accepting" this inlay hint.
+ pub text_edit: Option<TextEdit>,
}
impl InlayHint {
- fn closing_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") }
+ fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
+ InlayHint {
+ range,
+ kind,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ }
}
- fn opening_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") }
+ fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
+ InlayHint {
+ range,
+ kind,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: false,
+ }
}
}
@@ -283,14 +311,15 @@ impl InlayHintLabelBuilder<'_> {
fn label_of_ty(
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
- ty: hir::Type,
+ ty: &hir::Type,
) -> Option<InlayHintLabel> {
fn rec(
sema: &Semantics<'_, RootDatabase>,
famous_defs: &FamousDefs<'_, '_>,
mut max_length: Option<usize>,
- ty: hir::Type,
+ ty: &hir::Type,
label_builder: &mut InlayHintLabelBuilder<'_>,
+ config: &InlayHintsConfig,
) -> Result<(), HirDisplayError> {
let iter_item_type = hint_iterator(sema, famous_defs, &ty);
match iter_item_type {
@@ -321,11 +350,14 @@ fn label_of_ty(
label_builder.write_str(LABEL_ITEM)?;
label_builder.end_location_link();
label_builder.write_str(LABEL_MIDDLE2)?;
- rec(sema, famous_defs, max_length, ty, label_builder)?;
+ rec(sema, famous_defs, max_length, &ty, label_builder, config)?;
label_builder.write_str(LABEL_END)?;
Ok(())
}
- None => ty.display_truncated(sema.db, max_length).write_to(label_builder),
+ None => ty
+ .display_truncated(sema.db, max_length)
+ .with_closure_style(config.closure_style)
+ .write_to(label_builder),
}
}
@@ -335,11 +367,28 @@ fn label_of_ty(
location: None,
result: InlayHintLabel::default(),
};
- let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder);
+ let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config);
let r = label_builder.finish();
Some(r)
}
+fn ty_to_text_edit(
+ sema: &Semantics<'_, RootDatabase>,
+ node_for_hint: &SyntaxNode,
+ ty: &hir::Type,
+ offset_to_insert: TextSize,
+ prefix: String,
+) -> Option<TextEdit> {
+ let scope = sema.scope(node_for_hint)?;
+ // FIXME: Limit the length and bail out on excess somehow?
+ let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
+
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix);
+ builder.insert(offset_to_insert, rendered);
+ Some(builder.finish())
+}
+
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
@@ -408,10 +457,10 @@ fn hints(
ast::Expr::MethodCallExpr(it) => {
param_name::hints(hints, sema, config, ast::Expr::from(it))
}
- ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, famous_defs, config, file_id, it),
- // We could show reborrows for all expressions, but usually that is just noise to the user
- // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
- // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
+ ast::Expr::ClosureExpr(it) => {
+ closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
+ closure_ret::hints(hints, famous_defs, config, file_id, it)
+ },
_ => None,
}
},
@@ -481,6 +530,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
#[cfg(test)]
mod tests {
use expect_test::Expect;
+ use hir::ClosureStyle;
use itertools::Itertools;
use test_utils::extract_annotations;
@@ -498,12 +548,14 @@ mod tests {
chaining_hints: false,
lifetime_elision_hints: LifetimeElisionHints::Never,
closure_return_type_hints: ClosureReturnTypeHints::Never,
+ closure_capture_hints: false,
adjustment_hints: AdjustmentHints::Never,
adjustment_hints_mode: AdjustmentHintsMode::Prefix,
adjustment_hints_hide_outside_unsafe: false,
binding_mode_hints: false,
hide_named_constructor_hints: false,
hide_closure_initialization_hints: false,
+ closure_style: ClosureStyle::ImplFn,
param_names_for_lifetime_elision_hints: false,
max_length: None,
closing_brace_hints_min_lines: None,
@@ -530,7 +582,8 @@ mod tests {
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let actual = inlay_hints
.into_iter()
- .map(|it| (it.range, it.label.to_string()))
+ // FIXME: We trim the start because some inlay produces leading whitespace which is not properly supported by our annotation extraction
+ .map(|it| (it.range, it.label.to_string().trim_start().to_owned()))
.sorted_by_key(|(range, _)| range.start())
.collect::<Vec<_>>();
expected.sort_by_key(|(range, _)| range.start());
@@ -545,6 +598,37 @@ mod tests {
expect.assert_debug_eq(&inlay_hints)
}
+ /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
+ /// expect test.
+ #[track_caller]
+ pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+ let edits = inlay_hints
+ .into_iter()
+ .filter_map(|hint| hint.text_edit)
+ .reduce(|mut acc, next| {
+ acc.union(next).expect("merging text edits failed");
+ acc
+ })
+ .expect("no edit returned");
+
+ let mut actual = analysis.file_text(file_id).unwrap().to_string();
+ edits.apply(&mut actual);
+ expect.assert_eq(&actual);
+ }
+
+ #[track_caller]
+ pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+ let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+
+ assert!(edits.is_empty(), "unexpected edits: {edits:?}");
+ }
+
#[test]
fn hints_disabled() {
check_with_config(
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 46505b304..10bee2a6a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -3,7 +3,11 @@
//! let _: u32 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0;
//! ```
-use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics};
+use either::Either;
+use hir::{
+ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
+ Semantics,
+};
use ide_db::RootDatabase;
use stdx::never;
@@ -13,8 +17,8 @@ use syntax::{
};
use crate::{
- AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
- InlayTooltip,
+ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition,
+ InlayHintsConfig, InlayKind, InlayTooltip,
};
pub(super) fn hints(
@@ -60,22 +64,26 @@ pub(super) fn hints(
mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
if needs_outer_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
}
if postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
- let (mut tmp0, mut tmp1);
- let iter: &mut dyn Iterator<Item = _> = if postfix {
- tmp0 = adjustments.into_iter();
- &mut tmp0
+ let mut iter = if postfix {
+ Either::Left(adjustments.into_iter())
} else {
- tmp1 = adjustments.into_iter().rev();
- &mut tmp1
+ Either::Right(adjustments.into_iter().rev())
};
+ let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
for Adjustment { source, target, kind } in iter {
if source == target {
@@ -88,7 +96,13 @@ pub(super) fn hints(
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
("<never-to-any>", "never to any")
}
- Adjust::Deref(_) => ("*", "dereference"),
+ Adjust::Deref(None) => ("*", "dereference"),
+ Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
+ ("*", "`Deref` dereference")
+ }
+ Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
+ ("*", "`DerefMut` dereference")
+ }
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
@@ -125,7 +139,10 @@ pub(super) fn hints(
};
acc.push(InlayHint {
range: expr.syntax().text_range(),
- kind: if postfix { InlayKind::AdjustmentPostfix } else { InlayKind::Adjustment },
+ pad_left: false,
+ pad_right: false,
+ position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
+ kind: InlayKind::Adjustment,
label: InlayHintLabel::simple(
if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
Some(InlayTooltip::Markdown(format!(
@@ -135,19 +152,23 @@ pub(super) fn hints(
))),
None,
),
+ text_edit: None,
});
}
if !postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
if needs_outer_parens {
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
Some(())
}
-/// Returns whatever the hint should be postfix and if we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`,
/// if we are going to add (`postfix`) adjustments hints to it.
fn mode_and_needs_parens_for_adjustment_hints(
expr: &ast::Expr,
@@ -182,7 +203,7 @@ fn mode_and_needs_parens_for_adjustment_hints(
}
}
-/// Returns whatever we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
/// if we are going to add (`postfix`) adjustments hints to it.
fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
// This is a very miserable pile of hacks...
@@ -193,10 +214,10 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
// But we want to check what would happen if we add `*`/`.*` to the inner expression.
// To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
// to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
- // where "expr" is the `expr` parameter, `*expr` is the editted `expr`,
+ // where "expr" is the `expr` parameter, `*expr` is the edited `expr`,
// and "parent" is the parent of the original expression...
//
- // For this we utilize mutable mutable trees, which is a HACK, but it works.
+ // For this we utilize mutable trees, which is a HACK, but it works.
//
// FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this*
@@ -242,7 +263,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
};
// At this point
- // - `parent` is the parrent of the original expression
+ // - `parent` is the parent of the original expression
// - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
// - `expr` is the clone of the original expression (with `dummy_expr` as the parent)
@@ -264,7 +285,7 @@ mod tests {
check_with_config(
InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
fn main() {
let _: u32 = loop {};
//^^^^^^^<never-to-any>
@@ -315,6 +336,8 @@ fn main() {
(&Struct).consume();
//^^^^^^^*
(&Struct).by_ref();
+ //^^^^^^^&
+ //^^^^^^^*
(&mut Struct).consume();
//^^^^^^^^^^^*
@@ -322,6 +345,8 @@ fn main() {
//^^^^^^^^^^^&
//^^^^^^^^^^^*
(&mut Struct).by_ref_mut();
+ //^^^^^^^^^^^&mut $
+ //^^^^^^^^^^^*
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
@@ -360,6 +385,19 @@ fn main() {
(()) == {()};
// ^^&
// ^^^^&
+ let closure: dyn Fn = || ();
+ closure();
+ //^^^^^^^(
+ //^^^^^^^&
+ //^^^^^^^)
+ Struct[0];
+ //^^^^^^(
+ //^^^^^^&
+ //^^^^^^)
+ &mut Struct[0];
+ //^^^^^^(
+ //^^^^^^&mut $
+ //^^^^^^)
}
#[derive(Copy, Clone)]
@@ -369,8 +407,13 @@ impl Struct {
fn by_ref(&self) {}
fn by_ref_mut(&mut self) {}
}
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
"#,
- )
+ );
}
#[test]
@@ -382,7 +425,7 @@ impl Struct {
..DISABLED_CONFIG
},
r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
fn main() {
Struct.consume();
@@ -396,6 +439,10 @@ fn main() {
//^^^^^^^)
//^^^^^^^.*
(&Struct).by_ref();
+ //^^^^^^^(
+ //^^^^^^^)
+ //^^^^^^^.*
+ //^^^^^^^.&
(&mut Struct).consume();
//^^^^^^^^^^^(
@@ -407,6 +454,10 @@ fn main() {
//^^^^^^^^^^^.*
//^^^^^^^^^^^.&
(&mut Struct).by_ref_mut();
+ //^^^^^^^^^^^(
+ //^^^^^^^^^^^)
+ //^^^^^^^^^^^.*
+ //^^^^^^^^^^^.&mut
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
@@ -457,6 +508,13 @@ fn main() {
(()) == {()};
// ^^.&
// ^^^^.&
+ let closure: dyn Fn = || ();
+ closure();
+ //^^^^^^^.&
+ Struct[0];
+ //^^^^^^.&
+ &mut Struct[0];
+ //^^^^^^.&mut
}
#[derive(Copy, Clone)]
@@ -466,6 +524,11 @@ impl Struct {
fn by_ref(&self) {}
fn by_ref_mut(&mut self) {}
}
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
"#,
);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 6a5092733..07b9f9cc1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -3,7 +3,7 @@
//! fn f(a: i32, b: i32) -> i32 { a + b }
//! let _x /* i32 */= f(4, 4);
//! ```
-use hir::{Semantics, TypeInfo};
+use hir::Semantics;
use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
use itertools::Itertools;
@@ -12,9 +12,10 @@ use syntax::{
match_ast,
};
-use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind};
-
-use super::label_of_ty;
+use crate::{
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
+};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -27,15 +28,45 @@ pub(super) fn hints(
return None;
}
+ let parent = pat.syntax().parent()?;
+ let type_ascriptable = match_ast! {
+ match parent {
+ ast::Param(it) => {
+ if it.ty().is_some() {
+ return None;
+ }
+ Some(it.colon_token())
+ },
+ ast::LetStmt(it) => {
+ if config.hide_closure_initialization_hints {
+ if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
+ if closure_has_block_body(&closure) {
+ return None;
+ }
+ }
+ }
+ if it.ty().is_some() {
+ return None;
+ }
+ Some(it.colon_token())
+ },
+ _ => None
+ }
+ };
+
let descended = sema.descend_node_into_attributes(pat.clone()).pop();
let desc_pat = descended.as_ref().unwrap_or(pat);
- let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
+ let ty = sema.type_of_binding_in_pat(desc_pat)?;
- if should_not_display_type_hint(sema, config, pat, &ty) {
+ if ty.is_unknown() {
return None;
}
- let label = label_of_ty(famous_defs, config, ty)?;
+ if sema.resolve_bind_pat_to_const(pat).is_some() {
+ return None;
+ }
+
+ let mut label = label_of_ty(famous_defs, config, &ty)?;
if config.hide_named_constructor_hints
&& is_named_constructor(sema, pat, &label.to_string()).is_some()
@@ -43,69 +74,46 @@ pub(super) fn hints(
return None;
}
+ let text_edit = if let Some(colon_token) = &type_ascriptable {
+ ty_to_text_edit(
+ sema,
+ desc_pat.syntax(),
+ &ty,
+ colon_token
+ .as_ref()
+ .map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
+ .end(),
+ if colon_token.is_some() { String::new() } else { String::from(": ") },
+ )
+ } else {
+ None
+ };
+
+ let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_)));
+ if render_colons {
+ label.prepend_str(": ");
+ }
+
+ let text_range = match pat.name() {
+ Some(name) => name.syntax().text_range(),
+ None => pat.syntax().text_range(),
+ };
acc.push(InlayHint {
- range: match pat.name() {
- Some(name) => name.syntax().text_range(),
- None => pat.syntax().text_range(),
+ range: match type_ascriptable {
+ Some(Some(t)) => text_range.cover(t.text_range()),
+ _ => text_range,
},
kind: InlayKind::Type,
label,
+ text_edit,
+ position: InlayHintPosition::After,
+ pad_left: !render_colons,
+ pad_right: false,
});
Some(())
}
-fn should_not_display_type_hint(
- sema: &Semantics<'_, RootDatabase>,
- config: &InlayHintsConfig,
- bind_pat: &ast::IdentPat,
- pat_ty: &hir::Type,
-) -> bool {
- let db = sema.db;
-
- if pat_ty.is_unknown() {
- return true;
- }
-
- if sema.resolve_bind_pat_to_const(bind_pat).is_some() {
- return true;
- }
-
- for node in bind_pat.syntax().ancestors() {
- match_ast! {
- match node {
- ast::LetStmt(it) => {
- if config.hide_closure_initialization_hints {
- if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
- if closure_has_block_body(&closure) {
- return true;
- }
- }
- }
- return it.ty().is_some()
- },
- // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
- ast::Param(it) => return it.ty().is_some(),
- ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
- ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
- ast::IfExpr(_) => return false,
- ast::WhileExpr(_) => return false,
- ast::ForExpr(it) => {
- // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
- // Type of expr should be iterable.
- return it.in_token().is_none() ||
- it.iterable()
- .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
- .map(TypeInfo::original)
- .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
- },
- _ => (),
- }
- }
- }
- false
-}
-
fn is_named_constructor(
sema: &Semantics<'_, RootDatabase>,
pat: &ast::IdentPat,
@@ -159,30 +167,20 @@ fn is_named_constructor(
(ctor_name == ty_name).then_some(())
}
-fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
- if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
- let pat_text = bind_pat.to_string();
- enum_data
- .variants(db)
- .into_iter()
- .map(|variant| variant.name(db).to_smol_str())
- .any(|enum_name| enum_name == pat_text)
- } else {
- false
- }
-}
-
#[cfg(test)]
mod tests {
// This module also contains tests for super::closure_ret
+ use expect_test::expect;
+ use hir::ClosureStyle;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
- use crate::{fixture, inlay_hints::InlayHintsConfig};
+ use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
- use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
- use crate::ClosureReturnTypeHints;
+ use crate::inlay_hints::tests::{
+ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
+ };
#[track_caller]
fn check_types(ra_fixture: &str) {
@@ -235,7 +233,7 @@ fn main() {
let zz_ref = &zz;
//^^^^^^ &Test<i32>
let test = || zz;
- //^^^^ || -> Test<i32>
+ //^^^^ impl FnOnce() -> Test<i32>
}"#,
);
}
@@ -528,24 +526,7 @@ fn main() {
struct Test { a: Option<u32>, b: u8 }
fn main() {
- let test = Some(Test { a: Some(3), b: 1 });
- //^^^^ Option<Test>
- if let None = &test {};
- if let test = &test {};
- //^^^^ &Option<Test>
- if let Some(test) = &test {};
- //^^^^ &Test
- if let Some(Test { a, b }) = &test {};
- //^ &Option<u32> ^ &u8
- if let Some(Test { a: x, b: y }) = &test {};
- //^ &Option<u32> ^ &u8
- if let Some(Test { a: Some(x), b: y }) = &test {};
- //^ &u32 ^ &u8
- if let Some(Test { a: None, b: y }) = &test {};
- //^ &u8
- if let Some(Test { b: y, .. }) = &test {};
- //^ &u8
- if test == None {}
+
}"#,
);
}
@@ -560,8 +541,8 @@ struct Test { a: Option<u32>, b: u8 }
fn main() {
let test = Some(Test { a: Some(3), b: 1 });
//^^^^ Option<Test>
- while let Some(Test { a: Some(x), b: y }) = &test {};
- //^ &u32 ^ &u8
+ while let Some(Test { a: Some(x), b: y }) = &test {};
+ //^ &u32 ^ &u8
}"#,
);
}
@@ -753,7 +734,7 @@ fn main() {
let func = times2;
// ^^^^ fn times2(i32) -> i32
let closure = |x: i32| x * 2;
- // ^^^^^^^ |i32| -> i32
+ // ^^^^^^^ impl Fn(i32) -> i32
}
fn fallible() -> ControlFlow<()> {
@@ -811,49 +792,90 @@ fn fallible() -> ControlFlow<()> {
}
#[test]
- fn closures() {
- check(
+ fn closure_style() {
+ check_with_config(
+ InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
+//- minicore: fn
fn main() {
- let mut start = 0;
- //^^^^^ i32
- (0..2).for_each(|increment | { start += increment; });
- //^^^^^^^^^ i32
-
- let multiply =
- //^^^^^^^^ |i32, i32| -> i32
- | a, b| a * b
- //^ i32 ^ i32
-
- ;
-
- let _: i32 = multiply(1, 2);
- //^ a ^ b
- let multiply_ref = &multiply;
- //^^^^^^^^^^^^ &|i32, i32| -> i32
-
- let return_42 = || 42;
- //^^^^^^^^^ || -> i32
- || { 42 };
- //^^ i32
-}"#,
+ let x = || 2;
+ //^ impl Fn() -> i32
+ let y = |t: i32| x() + t;
+ //^ impl Fn(i32) -> i32
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ impl FnMut(i32)
+ let p = (y, z);
+ //^ (impl Fn(i32) -> i32, impl FnMut(i32))
+}
+ "#,
);
- }
-
- #[test]
- fn return_type_hints_for_closure_without_block() {
check_with_config(
InlayHintsConfig {
- closure_return_type_hints: ClosureReturnTypeHints::Always,
+ type_hints: true,
+ closure_style: ClosureStyle::RANotation,
..DISABLED_CONFIG
},
r#"
+//- minicore: fn
fn main() {
- let a = || { 0 };
- //^^ i32
- let b = || 0;
- //^^ i32
-}"#,
+ let x = || 2;
+ //^ || -> i32
+ let y = |t: i32| x() + t;
+ //^ |i32| -> i32
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ |i32| -> ()
+ let p = (y, z);
+ //^ (|i32| -> i32, |i32| -> ())
+}
+ "#,
+ );
+ check_with_config(
+ InlayHintsConfig {
+ type_hints: true,
+ closure_style: ClosureStyle::ClosureWithId,
+ ..DISABLED_CONFIG
+ },
+ r#"
+//- minicore: fn
+fn main() {
+ let x = || 2;
+ //^ {closure#0}
+ let y = |t: i32| x() + t;
+ //^ {closure#1}
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ {closure#2}
+ let p = (y, z);
+ //^ ({closure#1}, {closure#2})
+}
+ "#,
+ );
+ check_with_config(
+ InlayHintsConfig {
+ type_hints: true,
+ closure_style: ClosureStyle::Hide,
+ ..DISABLED_CONFIG
+ },
+ r#"
+//- minicore: fn
+fn main() {
+ let x = || 2;
+ //^ …
+ let y = |t: i32| x() + t;
+ //^ …
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ …
+ let p = (y, z);
+ //^ (…, …)
+}
+ "#,
);
}
@@ -871,13 +893,13 @@ fn main() {
let multiple_2 = |x: i32| { x * 2 };
let multiple_2 = |x: i32| x * 2;
- // ^^^^^^^^^^ |i32| -> i32
+ // ^^^^^^^^^^ impl Fn(i32) -> i32
let (not) = (|x: bool| { !x });
- // ^^^ |bool| -> bool
+ // ^^^ impl Fn(bool) -> bool
let (is_zero, _b) = (|x: usize| { x == 0 }, false);
- // ^^^^^^^ |usize| -> bool
+ // ^^^^^^^ impl Fn(usize) -> bool
// ^^ bool
let plus_one = |x| { x + 1 };
@@ -923,4 +945,160 @@ fn main() {
}"#,
);
}
+
+ #[test]
+ fn edit_for_let_stmt() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)) = v;
+ let a = f;
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a: S<(S<i32>, S<()>)> = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)): S<(S<i32>, S<()>)> = v;
+ let a: F = f;
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_param() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+fn test<T>(t: T) {
+ let f = |a, b, c| {};
+ let result = f(42, "", t);
+}
+"#,
+ expect![[r#"
+ fn test<T>(t: T) {
+ let f = |a: i32, b: &str, c: T| {};
+ let result: () = f(42, "", t);
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_ret() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || { 3 };
+ let f = |a: S<usize>| { S(a) };
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test() {
+ let f = || -> i32 { 3 };
+ let f = |a: S<usize>| -> S<S<usize>> { S(a) };
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_prefixes_paths() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+pub struct S<T>(T);
+mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a = make();
+ }
+}
+"#,
+ expect![[r#"
+ pub struct S<T>(T);
+ mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a: S<inner::S<i64>, crate::S<usize>> = make();
+ }
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+fn test() {
+ if let a = 42 {}
+ while let a = 42 {}
+ match 42 {
+ a => (),
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn no_edit_for_opaque_type() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+trait Trait {}
+struct S<T>(T);
+fn foo() -> impl Trait {}
+fn bar() -> S<impl Trait> {}
+fn test() {
+ let a = foo();
+ let a = bar();
+ let f = || { foo() };
+ let f = || { bar() };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_edit_for_closure_return_without_body_block() {
+ // We can lift this limitation; see FIXME in closure_ret module.
+ let config = InlayHintsConfig {
+ closure_return_type_hints: ClosureReturnTypeHints::Always,
+ ..TEST_CONFIG
+ };
+ check_no_edit(
+ config,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || 3;
+ let f = |a: S<usize>| S(a);
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 5d9729263..343cf17e5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -7,7 +7,7 @@ use ide_db::RootDatabase;
use syntax::ast::{self, AstNode};
-use crate::{InlayHint, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -49,7 +49,15 @@ pub(super) fn hints(
(true, false) => "&",
_ => return,
};
- acc.push(InlayHint { range, kind: InlayKind::BindingMode, label: r.to_string().into() });
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::BindingMode,
+ label: r.to_string().into(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: mut_reference,
+ });
});
match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
@@ -63,11 +71,21 @@ pub(super) fn hints(
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
label: bm.to_string().into(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
});
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
- acc.push(InlayHint::opening_paren(pat.syntax().text_range()));
- acc.push(InlayHint::closing_paren(pat.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::BindingMode,
+ pat.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(
+ InlayKind::BindingMode,
+ pat.syntax().text_range(),
+ ));
}
_ => (),
}
@@ -142,7 +160,6 @@ struct Struct {
field: &'static str,
}
fn foo(s @ Struct { field, .. }: &Struct) {}
- //^^^^^^^^^^^^^^^^^^^^^^^^ref
//^^^^^^^^^^^^^^^^^^^^&
//^^^^^ref
"#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 1e1771259..84eac16b9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -5,7 +5,7 @@ use syntax::{
Direction, NodeOrToken, SyntaxKind, T,
};
-use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
use super::label_of_ty;
@@ -60,7 +60,11 @@ pub(super) fn hints(
acc.push(InlayHint {
range: expr.syntax().text_range(),
kind: InlayKind::Chaining,
- label: label_of_ty(famous_defs, config, ty)?,
+ label: label_of_ty(famous_defs, config, &ty)?,
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: true,
+ pad_right: false,
});
}
}
@@ -103,6 +107,9 @@ fn main() {
[
InlayHint {
range: 147..172,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -120,9 +127,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 147..154,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -140,6 +151,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -188,6 +200,9 @@ fn main() {
[
InlayHint {
range: 143..190,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -205,9 +220,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -225,6 +244,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -257,6 +277,9 @@ fn main() {
[
InlayHint {
range: 143..190,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -274,9 +297,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -294,6 +321,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -327,6 +355,9 @@ fn main() {
[
InlayHint {
range: 246..283,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -357,9 +388,13 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
InlayHint {
range: 246..265,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -390,6 +425,7 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
]
"#]],
@@ -425,6 +461,9 @@ fn main() {
[
InlayHint {
range: 174..241,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -435,7 +474,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -448,16 +487,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..224,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -468,7 +511,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -481,16 +524,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..206,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -501,7 +548,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -514,16 +561,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..189,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"&mut ",
@@ -541,6 +592,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -573,6 +625,9 @@ fn main() {
[
InlayHint {
range: 124..130,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Type,
label: [
"",
@@ -590,9 +645,22 @@ fn main() {
},
"",
],
+ text_edit: Some(
+ TextEdit {
+ indels: [
+ Indel {
+ insert: ": Struct",
+ delete: 130..130,
+ },
+ ],
+ },
+ ),
},
InlayHint {
range: 145..185,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -610,9 +678,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 145..168,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -630,9 +702,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 222..228,
+ position: Before,
+ pad_left: false,
+ pad_right: true,
kind: Parameter,
label: [
InlayHintLabelPart {
@@ -648,6 +724,7 @@ fn main() {
tooltip: "",
},
],
+ text_edit: None,
},
]
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 14c11be54..2cefd5acd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -10,7 +10,7 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T,
};
-use crate::{FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -35,7 +35,7 @@ pub(super) fn hints(
let ty = imp.self_ty(sema.db);
let trait_ = imp.trait_(sema.db);
let hint_text = match trait_ {
- Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
+ Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)),
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
};
(hint_text, None)
@@ -112,6 +112,10 @@ pub(super) fn hints(
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: true,
+ pad_right: false,
});
None
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
new file mode 100644
index 000000000..9d5defcbb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -0,0 +1,207 @@
+//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
+use ide_db::{base_db::FileId, famous_defs::FamousDefs};
+use syntax::ast::{self, AstNode};
+use text_edit::{TextRange, TextSize};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ config: &InlayHintsConfig,
+ _file_id: FileId,
+ closure: ast::ClosureExpr,
+) -> Option<()> {
+ if !config.closure_capture_hints {
+ return None;
+ }
+ let ty = &sema.type_of_expr(&closure.clone().into())?.original;
+ let c = ty.as_closure()?;
+ let captures = c.captured_items(sema.db);
+
+ if captures.is_empty() {
+ return None;
+ }
+
+ let move_kw_range = match closure.move_token() {
+ Some(t) => t.text_range(),
+ None => {
+ let range = closure.syntax().first_token()?.prev_token()?.text_range();
+ let range = TextRange::new(range.end() - TextSize::from(1), range.end());
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple("move", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ range
+ }
+ };
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ let last = captures.len() - 1;
+ for (idx, capture) in captures.into_iter().enumerate() {
+ let local = capture.local();
+ let source = local.primary_source(sema.db);
+
+ // force cache the source file, otherwise sema lookup will potentially panic
+ _ = sema.parse_or_expand(source.file());
+
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(
+ format!(
+ "{}{}",
+ match capture.kind() {
+ hir::CaptureKind::SharedRef => "&",
+ hir::CaptureKind::UniqueSharedRef => "&unique ",
+ hir::CaptureKind::MutableRef => "&mut ",
+ hir::CaptureKind::Move => "",
+ },
+ capture.display_place(sema.db)
+ ),
+ None,
+ source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+ ),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+
+ if idx != last {
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(", ", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ }
+ }
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
+ });
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+ InlayHintsConfig,
+ };
+
+ #[test]
+ fn all_capture_kinds() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+
+
+#[derive(Copy, Clone)]
+struct Copy;
+
+struct NonCopy;
+
+fn main() {
+ let foo = Copy;
+ let bar = NonCopy;
+ let mut baz = NonCopy;
+ let qux = &mut NonCopy;
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ bar
+// ^ , $
+// ^ baz
+// ^ , $
+// ^ qux
+// ^ )
+ foo;
+ bar;
+ baz;
+ qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ &bar
+// ^ , $
+// ^ &baz
+// ^ , $
+// ^ &qux
+// ^ )
+ &foo;
+ &bar;
+ &baz;
+ &qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ )
+ &mut baz;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ , $
+// ^ &mut *qux
+// ^ )
+ baz = NonCopy;
+ *qux = NonCopy;
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_token() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+fn main() {
+ let foo = u32;
+ move || {
+// ^^^^ (
+// ^^^^ foo
+// ^^^^ )
+ foo;
+ };
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index f03a18b8e..3b41db0f1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -1,14 +1,14 @@
//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
use ide_db::{base_db::FileId, famous_defs::FamousDefs};
use syntax::ast::{self, AstNode};
use crate::{
- inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig,
- InlayKind,
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
};
-use super::label_of_ty;
-
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
@@ -20,29 +20,81 @@ pub(super) fn hints(
return None;
}
- if closure.ret_type().is_some() {
- return None;
- }
+ let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some()));
+ let arrow = match ret_type {
+ Some((_, true)) => return None,
+ Some((arrow, _)) => arrow,
+ None => None,
+ };
- if !closure_has_block_body(&closure)
- && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
- {
+ let has_block_body = closure_has_block_body(&closure);
+ if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock {
return None;
}
let param_list = closure.param_list()?;
let closure = sema.descend_node_into_attributes(closure).pop()?;
- let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
+ let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted();
let callable = ty.as_callable(sema.db)?;
let ty = callable.return_type();
- if ty.is_unit() {
+ if arrow.is_none() && ty.is_unit() {
return None;
}
+
+ let mut label = label_of_ty(famous_defs, config, &ty)?;
+
+ if arrow.is_none() {
+ label.prepend_str(" -> ");
+ }
+ // FIXME?: We could provide text edit to insert braces for closures with non-block body.
+ let text_edit = if has_block_body {
+ ty_to_text_edit(
+ sema,
+ closure.syntax(),
+ &ty,
+ arrow
+ .as_ref()
+ .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range())
+ .end(),
+ if arrow.is_none() { String::from(" -> ") } else { String::new() },
+ )
+ } else {
+ None
+ };
+
acc.push(InlayHint {
range: param_list.syntax().text_range(),
- kind: InlayKind::ClosureReturnType,
- label: label_of_ty(famous_defs, config, ty)?,
+ kind: InlayKind::Type,
+ label,
+ text_edit,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
});
Some(())
}
+
+#[cfg(test)]
+mod tests {
+ use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
+
+ use super::*;
+
+ #[test]
+ fn return_type_hints_for_closure_without_block() {
+ check_with_config(
+ InlayHintsConfig {
+ closure_return_type_hints: ClosureReturnTypeHints::Always,
+ ..DISABLED_CONFIG
+ },
+ r#"
+fn main() {
+ let a = || { 0 };
+ //^^ -> i32
+ let b = || 0;
+ //^^ -> i32
+}"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index 67eaa553a..c4d2ac75c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -9,7 +9,8 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
use syntax::ast::{self, AstNode, HasName};
use crate::{
- DiscriminantHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip,
+ DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
+ InlayTooltip,
};
pub(super) fn enum_hints(
@@ -19,21 +20,23 @@ pub(super) fn enum_hints(
_: FileId,
enum_: ast::Enum,
) -> Option<()> {
- let enabled = match config.discriminant_hints {
- DiscriminantHints::Always => true,
- DiscriminantHints::Fieldless => {
- !sema.to_def(&enum_)?.is_data_carrying(sema.db)
- || enum_.variant_list()?.variants().any(|v| v.expr().is_some())
- }
- DiscriminantHints::Never => false,
- };
- if !enabled {
+ if let DiscriminantHints::Never = config.discriminant_hints {
+ return None;
+ }
+
+ let def = sema.to_def(&enum_)?;
+ let data_carrying = def.is_data_carrying(sema.db);
+ if matches!(config.discriminant_hints, DiscriminantHints::Fieldless) && data_carrying {
+ return None;
+ }
+ // data carrying enums without a primitive repr have no stable discriminants
+ if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) {
return None;
}
for variant in enum_.variant_list()?.variants() {
variant_hints(acc, sema, &variant);
}
- None
+ Some(())
}
fn variant_hints(
@@ -41,10 +44,11 @@ fn variant_hints(
sema: &Semantics<'_, RootDatabase>,
variant: &ast::Variant,
) -> Option<()> {
- if variant.eq_token().is_some() {
+ if variant.expr().is_some() {
return None;
}
+ let eq_token = variant.eq_token();
let name = variant.name()?;
let descended = sema.descend_node_into_attributes(variant.clone()).pop();
@@ -52,34 +56,43 @@ fn variant_hints(
let v = sema.to_def(desc_pat)?;
let d = v.eval(sema.db);
+ let range = match variant.field_list() {
+ Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
+ None => name.syntax().text_range(),
+ };
+ let eq_ = if eq_token.is_none() { " =" } else { "" };
+ let label = InlayHintLabel::simple(
+ match d {
+ Ok(x) => {
+ if x >= 10 {
+ format!("{eq_} {x} ({x:#X})")
+ } else {
+ format!("{eq_} {x}")
+ }
+ }
+ Err(_) => format!("{eq_} ?"),
+ },
+ Some(InlayTooltip::String(match &d {
+ Ok(_) => "enum variant discriminant".into(),
+ Err(e) => format!("{e:?}").into(),
+ })),
+ None,
+ );
acc.push(InlayHint {
- range: match variant.field_list() {
- Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
- None => name.syntax().text_range(),
+ range: match eq_token {
+ Some(t) => range.cover(t.text_range()),
+ _ => range,
},
kind: InlayKind::Discriminant,
- label: InlayHintLabel::simple(
- match d {
- Ok(x) => {
- if x >= 10 {
- format!("{x} ({x:#X})")
- } else {
- format!("{x}")
- }
- }
- Err(_) => "?".into(),
- },
- Some(InlayTooltip::String(match &d {
- Ok(_) => "enum variant discriminant".into(),
- Err(e) => format!("{e:?}").into(),
- })),
- None,
- ),
+ label,
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
});
Some(())
}
-
#[cfg(test)]
mod tests {
use crate::inlay_hints::{
@@ -111,30 +124,30 @@ mod tests {
check_discriminants(
r#"
enum Enum {
- Variant,
-//^^^^^^^0
- Variant1,
-//^^^^^^^^1
- Variant2,
-//^^^^^^^^2
- Variant5 = 5,
- Variant6,
-//^^^^^^^^6
+ Variant,
+// ^^^^^^^ = 0$
+ Variant1,
+// ^^^^^^^^ = 1$
+ Variant2,
+// ^^^^^^^^ = 2$
+ Variant5 = 5,
+ Variant6,
+// ^^^^^^^^ = 6$
}
"#,
);
check_discriminants_fieldless(
r#"
enum Enum {
- Variant,
-//^^^^^^^0
- Variant1,
-//^^^^^^^^1
- Variant2,
-//^^^^^^^^2
- Variant5 = 5,
- Variant6,
-//^^^^^^^^6
+ Variant,
+// ^^^^^^^ = 0
+ Variant1,
+// ^^^^^^^^ = 1
+ Variant2,
+// ^^^^^^^^ = 2
+ Variant5 = 5,
+ Variant6,
+// ^^^^^^^^ = 6
}
"#,
);
@@ -144,26 +157,23 @@ enum Enum {
fn datacarrying_mixed() {
check_discriminants(
r#"
+#[repr(u8)]
enum Enum {
Variant(),
- //^^^^^^^^^0
+// ^^^^^^^^^ = 0
Variant1,
- //^^^^^^^^1
+// ^^^^^^^^ = 1
Variant2 {},
- //^^^^^^^^^^^2
+// ^^^^^^^^^^^ = 2
Variant3,
- //^^^^^^^^3
+// ^^^^^^^^ = 3
Variant5 = 5,
Variant6,
- //^^^^^^^^6
+// ^^^^^^^^ = 6
}
"#,
);
- }
-
- #[test]
- fn datacarrying_mixed_fieldless_set() {
- check_discriminants_fieldless(
+ check_discriminants(
r#"
enum Enum {
Variant(),
@@ -175,20 +185,20 @@ enum Enum {
}
"#,
);
+ }
+
+ #[test]
+ fn datacarrying_mixed_fieldless_set() {
check_discriminants_fieldless(
r#"
+#[repr(u8)]
enum Enum {
Variant(),
- //^^^^^^^^^0
Variant1,
- //^^^^^^^^1
Variant2 {},
- //^^^^^^^^^^^2
Variant3,
- //^^^^^^^^3
- Variant5 = 5,
+ Variant5,
Variant6,
- //^^^^^^^^6
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index b7182085b..5fce11b78 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -10,7 +10,7 @@ use syntax::{
SyntaxToken,
};
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -25,6 +25,10 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: label.into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
};
let param_list = func.param_list()?;
@@ -189,12 +193,20 @@ pub(super) fn hints(
if is_empty { "" } else { ", " }
)
.into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
range: func.name()?.syntax().text_range(),
kind: InlayKind::GenericParamList,
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
}),
}
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index 1122ee2e3..fc297a8d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -8,7 +8,7 @@ use syntax::{
SyntaxKind,
};
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -34,6 +34,10 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".to_owned().into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
});
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 9cdae6324..c4f43f411 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -10,7 +10,7 @@ use ide_db::{base_db::FileRange, RootDatabase};
use stdx::to_lower_snake_case;
use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};
-use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -31,16 +31,16 @@ pub(super) fn hints(
// Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?;
let (param_name, name_syntax) = match param.as_ref()? {
- Either::Left(pat) => ("self".to_string(), pat.name()),
+ Either::Left(pat) => (pat.name()?, pat.name()),
Either::Right(pat) => match pat {
- ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
+ ast::Pat::IdentPat(it) => (it.name()?, it.name()),
_ => return None,
},
};
Some((name_syntax, param_name, arg, range))
})
.filter(|(_, param_name, arg, _)| {
- !should_hide_param_name_hint(sema, &callable, param_name, arg)
+ !should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
})
.map(|(param, param_name, _, FileRange { range, .. })| {
let mut linked_location = None;
@@ -53,10 +53,17 @@ pub(super) fn hints(
}
}
+ let colon = if config.render_colons { ":" } else { "" };
+ let label =
+ InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
InlayHint {
range,
kind: InlayKind::Parameter,
- label: InlayHintLabel::simple(param_name, None, linked_location),
+ label,
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
}
});
diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
new file mode 100644
index 000000000..cbcbb4b09
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
@@ -0,0 +1,46 @@
+use hir::Semantics;
+use ide_db::base_db::SourceDatabaseExt;
+use ide_db::RootDatabase;
+use ide_db::{base_db::FilePosition, LineIndexDatabase};
+use std::{fmt::Write, time::Instant};
+use syntax::TextRange;
+use syntax::{algo::find_node_at_offset, ast, AstNode};
+
+// Feature: Interpret Function
+//
+// |===
+// | Editor | Action Name
+//
+// | VS Code | **rust-analyzer: Interpret Function**
+// |===
+pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String {
+ let start_time = Instant::now();
+ let mut result = find_and_interpret(db, position)
+ .unwrap_or_else(|| "Not inside a function body".to_string());
+ let duration = Instant::now() - start_time;
+ writeln!(result, "").unwrap();
+ writeln!(result, "----------------------").unwrap();
+ writeln!(result, " Finished in {}s", duration.as_secs_f32()).unwrap();
+ result
+}
+
+fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> {
+ let sema = Semantics::new(db);
+ let source_file = sema.parse(position.file_id);
+
+ let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
+ let def = match item {
+ ast::Item::Fn(it) => sema.to_def(&it)?,
+ _ => return None,
+ };
+ let span_formatter = |file_id, text_range: TextRange| {
+ let line_col = db.line_index(file_id).line_col(text_range.start());
+ let path = &db
+ .source_root(db.file_source_root(file_id))
+ .path_for_file(&file_id)
+ .map(|x| x.to_string());
+ let path = path.as_deref().unwrap_or("<unknown file>");
+ format!("file://{path}#{}:{}", line_col.line + 1, line_col.col)
+ };
+ Some(def.eval(db, span_formatter))
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 078b66dd3..f195f78b3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -56,20 +56,24 @@ mod typing;
mod view_crate_graph;
mod view_hir;
mod view_mir;
+mod interpret_function;
mod view_item_tree;
mod shuffle_crate_graph;
+mod fetch_crates;
-use std::sync::Arc;
+use std::ffi::OsStr;
use cfg::CfgOptions;
+use fetch_crates::CrateInfo;
use ide_db::{
base_db::{
salsa::{self, ParallelDatabase},
CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
},
- symbol_index, LineIndexDatabase,
+ symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
};
use syntax::SourceFile;
+use triomphe::Arc;
use crate::navigation_target::{ToNav, TryToNav};
@@ -80,11 +84,14 @@ pub use crate::{
file_structure::{StructureNode, StructureNodeKind},
folding_ranges::{Fold, FoldKind},
highlight_related::{HighlightRelatedConfig, HighlightedRange},
- hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
+ hover::{
+ HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
+ MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+ },
inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayHintsConfig, InlayKind, InlayTooltip,
- LifetimeElisionHints,
+ InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+ InlayTooltip, LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
@@ -154,7 +161,11 @@ impl AnalysisHost {
}
pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
- self.db.update_lru_capacity(lru_capacity);
+ self.db.update_parse_query_lru_capacity(lru_capacity);
+ }
+
+ pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
+ self.db.update_lru_capacities(lru_capacities);
}
/// Returns a snapshot of the current state, which you can query for
@@ -170,7 +181,7 @@ impl AnalysisHost {
}
/// NB: this clears the database
- pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes)> {
+ pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
self.db.per_query_memory_usage()
}
pub fn request_cancellation(&mut self) {
@@ -233,14 +244,14 @@ impl Analysis {
None,
None,
cfg_options.clone(),
- cfg_options,
+ None,
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("Analysis::from_single_file has no target layout".into()),
+ None,
);
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
change.set_crate_graph(crate_graph);
host.apply_change(change);
(host.analysis(), file_id)
@@ -259,7 +270,7 @@ impl Analysis {
}
/// Gets the text of the source file.
- pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
+ pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
self.with_db(|db| db.file_text(file_id))
}
@@ -313,6 +324,10 @@ impl Analysis {
self.with_db(|db| view_mir::view_mir(db, position))
}
+ pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
+ self.with_db(|db| interpret_function::interpret_function(db, position))
+ }
+
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
}
@@ -322,6 +337,10 @@ impl Analysis {
self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
}
+ pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
+ self.with_db(|db| fetch_crates::fetch_crates(db))
+ }
+
pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
self.with_db(|db| expand_macro::expand_macro(db, position))
}
@@ -452,12 +471,19 @@ impl Analysis {
self.with_db(|db| moniker::moniker(db, position))
}
- /// Return URL(s) for the documentation of the symbol under the cursor.
+ /// Returns URL(s) for the documentation of the symbol under the cursor.
+ /// # Arguments
+ /// * `position` - Position in the file.
+ /// * `target_dir` - Directory where the build output is storeda.
pub fn external_docs(
&self,
position: FilePosition,
- ) -> Cancellable<Option<doc_links::DocumentationLink>> {
- self.with_db(|db| doc_links::external_docs(db, &position))
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+ ) -> Cancellable<doc_links::DocumentationLinks> {
+ self.with_db(|db| {
+ doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+ })
}
/// Computes parameter information at the given position.
@@ -508,6 +534,11 @@ impl Analysis {
self.with_db(|db| db.crate_graph()[crate_id].edition)
}
+ /// Returns true if this crate has `no_std` or `no_core` specified.
+ pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable<bool> {
+ self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std())
+ }
+
/// Returns the root file of the given crate.
pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 349e79ecf..0d57e63d2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -1,7 +1,7 @@
//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
//! for LSIF and LSP.
-use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
+use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics};
use ide_db::{
base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
defs::{Definition, IdentClass},
@@ -27,7 +27,7 @@ pub enum MonikerDescriptorKind {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerDescriptor {
- pub name: Name,
+ pub name: String,
pub desc: MonikerDescriptorKind,
}
@@ -41,11 +41,7 @@ impl ToString for MonikerIdentifier {
fn to_string(&self) -> String {
match self {
MonikerIdentifier { description, crate_name } => {
- format!(
- "{}::{}",
- crate_name,
- description.iter().map(|x| x.name.to_string()).join("::")
- )
+ format!("{}::{}", crate_name, description.iter().map(|x| &x.name).join("::"))
}
}
}
@@ -136,7 +132,10 @@ pub(crate) fn def_to_moniker(
let krate = module.krate();
let mut description = vec![];
description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
- Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
+ Some(MonikerDescriptor {
+ name: x.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Namespace,
+ })
}));
// Handle associated items within a trait
@@ -147,7 +146,7 @@ pub(crate) fn def_to_moniker(
// Because different traits can have functions with the same name,
// we have to include the trait name as part of the moniker for uniqueness.
description.push(MonikerDescriptor {
- name: trait_.name(db),
+ name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -156,14 +155,14 @@ pub(crate) fn def_to_moniker(
// we add both the struct name and the trait name to the path
if let Some(adt) = impl_.self_ty(db).as_adt() {
description.push(MonikerDescriptor {
- name: adt.name(db),
+ name: adt.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
if let Some(trait_) = impl_.trait_(db) {
description.push(MonikerDescriptor {
- name: trait_.name(db),
+ name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -173,7 +172,7 @@ pub(crate) fn def_to_moniker(
if let Definition::Field(it) = def {
description.push(MonikerDescriptor {
- name: it.parent_def(db).name(db),
+ name: it.parent_def(db).name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -191,48 +190,63 @@ pub(crate) fn def_to_moniker(
return None;
}
- MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
- }
- Definition::Macro(m) => {
- MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
- }
- Definition::Function(f) => {
- MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
- }
- Definition::Variant(v) => {
- MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::Const(c) => {
- MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
- }
- Definition::Trait(trait_) => {
- MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::TraitAlias(ta) => {
- MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::TypeAlias(ta) => {
- MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
- }
- Definition::Module(m) => {
- MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
- }
- Definition::BuiltinType(b) => {
- MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
+ MonikerDescriptor {
+ name: local.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Parameter,
+ }
}
+ Definition::Macro(m) => MonikerDescriptor {
+ name: m.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Macro,
+ },
+ Definition::Function(f) => MonikerDescriptor {
+ name: f.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Method,
+ },
+ Definition::Variant(v) => MonikerDescriptor {
+ name: v.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::Const(c) => MonikerDescriptor {
+ name: c.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Term,
+ },
+ Definition::Trait(trait_) => MonikerDescriptor {
+ name: trait_.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::TraitAlias(ta) => MonikerDescriptor {
+ name: ta.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::TypeAlias(ta) => MonikerDescriptor {
+ name: ta.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::TypeParameter,
+ },
+ Definition::Module(m) => MonikerDescriptor {
+ name: m.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Namespace,
+ },
+ Definition::BuiltinType(b) => MonikerDescriptor {
+ name: b.name().display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
Definition::SelfType(imp) => MonikerDescriptor {
- name: imp.self_ty(db).as_adt()?.name(db),
+ name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
- Definition::Field(it) => {
- MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
- }
- Definition::Adt(adt) => {
- MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::Static(s) => {
- MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
- }
+ Definition::Field(it) => MonikerDescriptor {
+ name: it.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Term,
+ },
+ Definition::Adt(adt) => MonikerDescriptor {
+ name: adt.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::Static(s) => MonikerDescriptor {
+ name: s.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Meta,
+ },
};
description.push(name_desc);
@@ -245,11 +259,17 @@ pub(crate) fn def_to_moniker(
kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
package_information: {
let (name, repo, version) = match krate.origin(db) {
- CrateOrigin::CratesIo { repo, name } => (
+ CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)),
+ CrateOrigin::Local { repo, name } => (
name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
repo,
krate.version(db),
),
+ CrateOrigin::Rustc { name } => (
+ name.clone(),
+ Some("https://github.com/rust-lang/rust/".to_string()),
+ Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)),
+ ),
CrateOrigin::Lang(lang) => (
krate.display_name(db)?.canonical_name().to_string(),
Some("https://github.com/rust-lang/rust/".to_string()),
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 6aae82f98..385c1b0c0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -4,8 +4,8 @@ use std::fmt;
use either::Either;
use hir::{
- symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
- InFile, LocalSource, ModuleSource, Semantics,
+ symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource,
+ HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
};
use ide_db::{
base_db::{FileId, FileRange},
@@ -15,7 +15,7 @@ use ide_db::{defs::Definition, RootDatabase};
use stdx::never;
use syntax::{
ast::{self, HasName},
- match_ast, AstNode, SmolStr, SyntaxNode, TextRange,
+ AstNode, SmolStr, SyntaxNode, TextRange,
};
/// `NavigationTarget` represents an element in the editor's UI which you can
@@ -45,6 +45,9 @@ pub struct NavigationTarget {
pub container_name: Option<SmolStr>,
pub description: Option<String>,
pub docs: Option<Documentation>,
+ /// In addition to a `name` field, a `NavigationTarget` may also be aliased
+ /// In such cases we want a `NavigationTarget` to be accessible by its alias
+ pub alias: Option<SmolStr>,
}
impl fmt::Debug for NavigationTarget {
@@ -89,10 +92,9 @@ impl NavigationTarget {
pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
- if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) {
- let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db);
- let focus_range =
- value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax()));
+ if let Some(InFile { value, file_id }) = &module.declaration_source(db) {
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, *file_id, value.syntax(), value.name());
let mut res = NavigationTarget::from_syntax(
file_id,
name,
@@ -128,14 +130,15 @@ impl NavigationTarget {
/// Allows `NavigationTarget` to be created from a `NameOwner`
pub(crate) fn from_named(
db: &RootDatabase,
- node @ InFile { file_id, value }: InFile<&dyn ast::HasName>,
+ InFile { file_id, value }: InFile<&dyn ast::HasName>,
kind: SymbolKind,
) -> NavigationTarget {
let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
- let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db);
- NavigationTarget::from_syntax(file_id, name, focus_range, range, kind)
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.name());
+
+ NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind)
}
fn from_syntax(
@@ -154,23 +157,43 @@ impl NavigationTarget {
container_name: None,
description: None,
docs: None,
+ alias: None,
}
}
}
impl TryToNav for FileSymbol {
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
- let full_range = self.loc.original_range(db)?;
- let name_range = self.loc.original_name_range(db)?;
+ let full_range = self.loc.original_range(db);
+ let focus_range = self.loc.original_name_range(db).and_then(|it| {
+ if it.file_id == full_range.file_id {
+ Some(it.range)
+ } else {
+ None
+ }
+ });
Some(NavigationTarget {
file_id: full_range.file_id,
- name: self.name.clone(),
- kind: Some(self.kind.into()),
+ name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
+ alias: if self.is_alias { Some(self.name.clone()) } else { None },
+ kind: Some(hir::ModuleDefId::from(self.def).into()),
full_range: full_range.range,
- focus_range: Some(name_range.range),
+ focus_range,
container_name: self.container_name.clone(),
- description: description_from_symbol(db, self),
+ description: match self.def {
+ hir::ModuleDef::Module(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Function(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Const(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Static(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::BuiltinType(_) => None,
+ },
docs: None,
})
}
@@ -221,38 +244,80 @@ impl TryToNav for hir::ModuleDef {
}
}
-pub(crate) trait ToNavFromAst {
+pub(crate) trait ToNavFromAst: Sized {
const KIND: SymbolKind;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ _ = db;
+ None
+ }
}
+
+fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option<SmolStr> {
+ match t.container(db) {
+ hir::ItemContainer::Trait(it) => Some(it.name(db).to_smol_str()),
+ // FIXME: Handle owners of blocks correctly here
+ hir::ItemContainer::Module(it) => it.name(db).map(|name| name.to_smol_str()),
+ _ => None,
+ }
+}
+
impl ToNavFromAst for hir::Function {
const KIND: SymbolKind = SymbolKind::Function;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
+
impl ToNavFromAst for hir::Const {
const KIND: SymbolKind = SymbolKind::Const;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Static {
const KIND: SymbolKind = SymbolKind::Static;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Struct {
const KIND: SymbolKind = SymbolKind::Struct;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Enum {
const KIND: SymbolKind = SymbolKind::Enum;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Variant {
const KIND: SymbolKind = SymbolKind::Variant;
}
impl ToNavFromAst for hir::Union {
const KIND: SymbolKind = SymbolKind::Union;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::TypeAlias {
const KIND: SymbolKind = SymbolKind::TypeAlias;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Trait {
const KIND: SymbolKind = SymbolKind::Trait;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::TraitAlias {
const KIND: SymbolKind = SymbolKind::TraitAlias;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl<D> TryToNav for D
@@ -269,6 +334,7 @@ where
);
res.docs = self.docs(db);
res.description = Some(self.display(db).to_string());
+ res.container_name = self.container_name(db);
Some(res)
}
}
@@ -280,15 +346,11 @@ impl ToNav for hir::Module {
let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
let (syntax, focus) = match &value {
ModuleSource::SourceFile(node) => (node.syntax(), None),
- ModuleSource::Module(node) => (
- node.syntax(),
- node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())),
- ),
+ ModuleSource::Module(node) => (node.syntax(), node.name()),
ModuleSource::BlockExpr(node) => (node.syntax(), None),
};
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, syntax).original_file_range(db);
- NavigationTarget::from_syntax(file_id, name, focus, full_range, SymbolKind::Module)
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+ NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module)
}
}
@@ -297,17 +359,14 @@ impl TryToNav for hir::Impl {
let InFile { file_id, value } = self.source(db)?;
let derive_attr = self.is_builtin_derive(db);
- let focus_range = if derive_attr.is_some() {
- None
- } else {
- value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax()))
- };
+ let focus = if derive_attr.is_some() { None } else { value.self_ty() };
- let FileRange { file_id, range: full_range } = match &derive_attr {
- Some(attr) => attr.syntax().original_file_range(db),
- None => InFile::new(file_id, value.syntax()).original_file_range(db),
+ let syntax = match &derive_attr {
+ Some(attr) => attr.value.syntax(),
+ None => value.syntax(),
};
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
Some(NavigationTarget::from_syntax(
file_id,
"impl".into(),
@@ -396,9 +455,8 @@ impl ToNav for LocalSource {
Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
Either::Right(it) => (it.syntax(), it.name()),
};
- let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, node).original_file_range(db);
+
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name);
let name = local.name(db).to_smol_str();
let kind = if local.is_self(db) {
@@ -411,6 +469,7 @@ impl ToNav for LocalSource {
NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(kind),
full_range,
focus_range,
@@ -432,13 +491,13 @@ impl ToNav for hir::Label {
let InFile { file_id, value } = self.source(db);
let name = self.name(db).to_smol_str();
- let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
- let FileRange { file_id, range: full_range } = range(value.syntax());
- let focus_range = value.lifetime().map(|lt| range(lt.syntax()).range);
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.lifetime());
NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::Label),
full_range,
focus_range,
@@ -463,22 +522,18 @@ impl TryToNav for hir::TypeParam {
Either::Right(x) => Either::Right(x),
};
- let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
- let focus_range = |syntax: &_| InFile::new(file_id, syntax).original_file_range_opt(db);
- let FileRange { file_id, range: full_range } = match &value {
- Either::Left(type_param) => range(type_param.syntax()),
- Either::Right(trait_) => trait_
- .name()
- .and_then(|name| focus_range(name.syntax()))
- .unwrap_or_else(|| range(trait_.syntax())),
+ let syntax = match &value {
+ Either::Left(type_param) => type_param.syntax(),
+ Either::Right(trait_) => trait_.syntax(),
};
- let focus_range = value
- .either(|it| it.name(), |it| it.name())
- .and_then(|it| focus_range(it.syntax()))
- .map(|it| it.range);
+ let focus = value.as_ref().either(|it| it.name(), |it| it.name());
+
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::TypeParam),
full_range,
focus_range,
@@ -500,14 +555,15 @@ impl TryToNav for hir::LifetimeParam {
let InFile { file_id, value } = self.source(db)?;
let name = self.name(db).to_smol_str();
- let FileRange { file_id, range: full_range } =
+ let FileRange { file_id, range } =
InFile::new(file_id, value.syntax()).original_file_range(db);
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::LifetimeParam),
- full_range,
- focus_range: Some(full_range),
+ full_range: range,
+ focus_range: Some(range),
container_name: None,
description: None,
docs: None,
@@ -528,12 +584,12 @@ impl TryToNav for hir::ConstParam {
}
};
- let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, value.syntax()).original_file_range(db);
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.name());
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::ConstParam),
full_range,
focus_range,
@@ -544,38 +600,19 @@ impl TryToNav for hir::ConstParam {
}
}
-/// Get a description of a symbol.
-///
-/// e.g. `struct Name`, `enum Name`, `fn Name`
-pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
- let sema = Semantics::new(db);
- let node = symbol.loc.syntax(&sema)?;
-
- match_ast! {
- match node {
- ast::Fn(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Static(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::RecordField(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Variant(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Union(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- _ => None,
- }
- }
-}
-
-fn orig_focus_range(
+fn orig_range_with_focus(
db: &RootDatabase,
- file_id: hir::HirFileId,
- syntax: &SyntaxNode,
-) -> Option<TextRange> {
- InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range)
+ hir_file: HirFileId,
+ value: &SyntaxNode,
+ name: Option<impl AstNode>,
+) -> (FileId, TextRange, Option<TextRange>) {
+ let FileRange { file_id, range: full_range } =
+ InFile::new(hir_file, value).original_file_range(db);
+ let focus_range = name
+ .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db))
+ .and_then(|range| if range.file_id == file_id { Some(range.range) } else { None });
+
+ (file_id, full_range, focus_range)
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
index 87b3ef380..d704d12a0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
@@ -12,9 +12,8 @@ use ide_db::{
salsa::{Database, ParallelDatabase, Snapshot},
Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
},
- FxIndexMap,
+ FxHashSet, FxIndexMap,
};
-use stdx::hash::NoHashHashSet;
use crate::RootDatabase;
@@ -81,7 +80,11 @@ pub(crate) fn parallel_prime_caches(
for _ in 0..num_worker_threads {
let worker = prime_caches_worker.clone();
let db = db.snapshot();
- std::thread::spawn(move || Cancelled::catch(|| worker(db)));
+
+ stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
+ .allow_leak(true)
+ .spawn(move || Cancelled::catch(|| worker(db)))
+ .expect("failed to spawn thread");
}
(work_sender, progress_receiver)
@@ -142,7 +145,7 @@ pub(crate) fn parallel_prime_caches(
}
}
-fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
// We're only interested in the workspace crates and the `ImportMap`s of their direct
// dependencies, though in practice the latter also compute the `DefMap`s.
// We don't prime transitive dependencies because they're generally not visible in
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 3684c1033..fdc5261ac 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -17,7 +17,7 @@ use ide_db::{
RootDatabase,
};
use itertools::Itertools;
-use stdx::hash::NoHashHashMap;
+use nohash_hasher::IntMap;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName},
@@ -31,7 +31,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
- pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+ pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
}
#[derive(Debug, Clone)]
@@ -715,7 +715,7 @@ fn f() {
}
"#,
expect![[r#"
- Foo Struct FileId(1) 17..51 28..31
+ Foo Struct FileId(1) 17..51 28..31 foo
FileId(0) 53..56
FileId(2) 79..82
@@ -803,7 +803,7 @@ pub(super) struct Foo$0 {
}
"#,
expect![[r#"
- Foo Struct FileId(2) 0..41 18..21
+ Foo Struct FileId(2) 0..41 18..21 some
FileId(1) 20..23 Import
FileId(1) 47..50
@@ -1289,7 +1289,7 @@ trait Foo where Self$0 {
impl Foo for () {}
"#,
expect![[r#"
- Self TypeParam FileId(0) 6..9 6..9
+ Self TypeParam FileId(0) 0..44 6..9
FileId(0) 16..20
FileId(0) 37..41
@@ -1380,7 +1380,7 @@ fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
trait Foo = where Self$0: ;
"#,
expect![[r#"
- Self TypeParam FileId(0) 6..9 6..9
+ Self TypeParam FileId(0) 0..25 6..9
FileId(0) 18..22
"#]],
@@ -1542,7 +1542,7 @@ fn f() {
FileId(0) 161..165
- func Function FileId(0) 137..146 140..144
+ func Function FileId(0) 137..146 140..144 module
FileId(0) 181..185
"#]],
@@ -1581,7 +1581,7 @@ trait Trait {
}
"#,
expect![[r#"
- func Function FileId(0) 48..87 51..55
+ func Function FileId(0) 48..87 51..55 Trait
FileId(0) 74..78
"#]],
@@ -1692,7 +1692,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- CONST Const FileId(0) 18..37 24..29
+ CONST Const FileId(0) 18..37 24..29 Trait
FileId(0) 71..76
FileId(0) 125..130
@@ -1721,7 +1721,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- TypeAlias TypeAlias FileId(0) 18..33 23..32
+ TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
@@ -1750,7 +1750,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- function Function FileId(0) 18..34 21..29
+ function Function FileId(0) 18..34 21..29 Trait
FileId(0) 65..73
FileId(0) 112..120
@@ -1894,7 +1894,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- TypeAlias TypeAlias FileId(0) 18..33 23..32
+ TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
@@ -1950,7 +1950,7 @@ impl Foo for Bar {
fn method() {}
"#,
expect![[r#"
- method Function FileId(0) 16..39 19..25
+ method Function FileId(0) 16..39 19..25 Foo
FileId(0) 101..107
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 8a8a9151c..27ad63d82 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -349,8 +349,13 @@ pub(crate) fn runnable_mod(
if !has_test_function_or_multiple_test_submodules(sema, &def) {
return None;
}
- let path =
- def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+ let path = def
+ .path_to_root(sema.db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(sema.db))
+ .map(|it| it.display(sema.db).to_string())
+ .join("::");
let attrs = def.attrs(sema.db);
let cfg = attrs.cfg();
@@ -376,7 +381,7 @@ pub(crate) fn runnable_impl(
} else {
String::new()
};
- let mut test_id = format!("{adt_name}{params}");
+ let mut test_id = format!("{}{params}", adt_name.display(sema.db));
test_id.retain(|c| c != ' ');
let test_id = TestId::Path(test_id);
@@ -391,8 +396,13 @@ fn runnable_mod_outline_definition(
if !has_test_function_or_multiple_test_submodules(sema, &def) {
return None;
}
- let path =
- def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+ let path = def
+ .path_to_root(sema.db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(sema.db))
+ .map(|it| it.display(sema.db).to_string())
+ .join("::");
let attrs = def.attrs(sema.db);
let cfg = attrs.cfg();
@@ -430,7 +440,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
let mut path = String::new();
def.canonical_module_path(db)?
.flat_map(|it| it.name(db))
- .for_each(|name| format_to!(path, "{}::", name));
+ .for_each(|name| format_to!(path, "{}::", name.display(db)));
// This probably belongs to canonical_path?
if let Some(assoc_item) = def.as_assoc_item(db) {
if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
@@ -438,17 +448,17 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
if let Some(adt) = ty.as_adt() {
let name = adt.name(db);
let mut ty_args = ty.generic_parameters(db).peekable();
- format_to!(path, "{}", name);
+ format_to!(path, "{}", name.display(db));
if ty_args.peek().is_some() {
format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
}
- format_to!(path, "::{}", def_name);
+ format_to!(path, "::{}", def_name.display(db));
path.retain(|c| c != ' ');
return Some(path);
}
}
}
- format_to!(path, "{}", def_name);
+ format_to!(path, "{}", def_name.display(db));
Some(path)
})();
@@ -2232,14 +2242,14 @@ mod tests {
file_id: FileId(
0,
),
- full_range: 52..115,
- focus_range: 67..75,
- name: "foo_test",
+ full_range: 121..185,
+ focus_range: 136..145,
+ name: "foo2_test",
kind: Function,
},
kind: Test {
test_id: Path(
- "tests::foo_test",
+ "tests::foo2_test",
),
attr: TestAttr {
ignore: false,
@@ -2253,14 +2263,14 @@ mod tests {
file_id: FileId(
0,
),
- full_range: 121..185,
- focus_range: 136..145,
- name: "foo2_test",
+ full_range: 52..115,
+ focus_range: 67..75,
+ name: "foo_test",
kind: Function,
},
kind: Test {
test_id: Path(
- "tests::foo2_test",
+ "tests::foo_test",
),
attr: TestAttr {
ignore: false,
@@ -2579,6 +2589,7 @@ mod r#mod {
),
full_range: 47..84,
name: "r#for",
+ container_name: "r#mod",
},
kind: DocTest {
test_id: Path(
@@ -2595,6 +2606,7 @@ mod r#mod {
),
full_range: 90..146,
name: "r#struct",
+ container_name: "r#mod",
},
kind: DocTest {
test_id: Path(
diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
index e606072a8..f85700daf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
@@ -1,9 +1,8 @@
-use std::sync::Arc;
-
use ide_db::{
- base_db::{salsa::Durability, CrateGraph, SourceDatabase},
+ base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase},
FxHashMap, RootDatabase,
};
+use triomphe::Arc;
// Feature: Shuffle Crate Graph
//
@@ -16,6 +15,7 @@ use ide_db::{
// |===
pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
let crate_graph = db.crate_graph();
+ let proc_macros = db.proc_macros();
let mut shuffled_ids = crate_graph.iter().collect::<Vec<_>>();
@@ -23,6 +23,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize);
let mut new_graph = CrateGraph::default();
+ let mut new_proc_macros = ProcMacros::default();
let mut map = FxHashMap::default();
for old_id in shuffled_ids.iter().copied() {
@@ -35,11 +36,12 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
data.cfg_options.clone(),
data.potential_cfg_options.clone(),
data.env.clone(),
- data.proc_macro.clone(),
data.is_proc_macro,
data.origin.clone(),
data.target_layout.clone(),
+ data.channel,
);
+ new_proc_macros.insert(new_id, proc_macros[&old_id].clone());
map.insert(old_id, new_id);
}
@@ -53,4 +55,5 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
}
db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH);
+ db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 4b2c139f6..7795be54e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -15,8 +15,9 @@ use ide_db::{
use stdx::format_to;
use syntax::{
algo,
- ast::{self, HasArgList},
- match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize,
+ ast::{self, AstChildren, HasArgList},
+ match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
+ TextRange, TextSize, T,
};
use crate::RootDatabase;
@@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
}
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
},
+ ast::TuplePat(tuple_pat) => {
+ let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_pat(&sema, tuple_pat, token);
+ },
+ ast::TupleExpr(tuple_expr) => {
+ let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_expr(&sema, tuple_expr, token);
+ },
_ => (),
}
}
@@ -162,7 +177,7 @@ fn signature_help_for_call(
match callable.kind() {
hir::CallableKind::Function(func) => {
res.doc = func.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", func.name(db));
+ format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
None => func.assoc_fn_params(db),
@@ -170,15 +185,15 @@ fn signature_help_for_call(
}
hir::CallableKind::TupleStruct(strukt) => {
res.doc = strukt.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", strukt.name(db));
+ format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
res.doc = variant.docs(db).map(|it| it.into());
format_to!(
res.signature,
"enum {}::{}",
- variant.parent_enum(db).name(db),
- variant.name(db)
+ variant.parent_enum(db).name(db).display(db),
+ variant.name(db).display(db)
);
}
hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
@@ -248,31 +263,31 @@ fn signature_help_for_generics(
match generics_def {
hir::GenericDef::Function(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", it.name(db));
+ format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}", it.name(db));
+ format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", it.name(db));
+ format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {}", it.name(db));
+ format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "type {}", it.name(db));
+ format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
@@ -280,7 +295,7 @@ fn signature_help_for_generics(
// We'll use the signature of the enum, but include the docs of the variant.
res.doc = it.docs(db).map(|it| it.into());
let enum_ = it.parent_enum(db);
- format_to!(res.signature, "enum {}", enum_.name(db));
+ format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
}
// These don't have generic args that can be specified
@@ -395,24 +410,26 @@ fn signature_help_for_tuple_struct_pat(
pat: ast::TupleStructPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
- let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
- let is_left_of_rest_pat =
- rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
-
+ let path = pat.path()?;
+ let path_res = sema.resolve_path(&path)?;
let mut res = SignatureHelp {
doc: None,
signature: String::new(),
parameters: vec![],
active_parameter: None,
};
-
let db = sema.db;
- let path_res = sema.resolve_path(&pat.path()?)?;
+
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} (",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
variant.fields(db)
} else {
let adt = match path_res {
@@ -424,36 +441,78 @@ fn signature_help_for_tuple_struct_pat(
match adt {
hir::Adt::Struct(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} (", it.name(db));
+ format_to!(res.signature, "struct {} (", it.name(db).display(db));
it.fields(db)
}
_ => return None,
}
};
- let commas = pat
- .syntax()
- .children_with_tokens()
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,]);
- res.active_parameter = Some(if is_left_of_rest_pat {
- commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
- } else {
- let n_commas = commas
- .collect::<Vec<_>>()
- .into_iter()
- .rev()
- .take_while(|t| t.text_range().start() > token.text_range().start())
- .count();
- fields.len().saturating_sub(1).saturating_sub(n_commas)
- });
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ res,
+ pat.syntax(),
+ token,
+ pat.fields(),
+ fields.into_iter().map(|it| it.ty(db)),
+ ))
+}
+
+fn signature_help_for_tuple_pat(
+ sema: &Semantics<'_, RootDatabase>,
+ pat: ast::TuplePat,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let db = sema.db;
+ let field_pats = pat.fields();
+ let pat = pat.into();
+ let ty = sema.type_of_pat(&pat)?;
+ let fields = ty.original.tuple_fields(db);
+
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter: None,
+ },
+ pat.syntax(),
+ token,
+ field_pats,
+ fields.into_iter(),
+ ))
+}
+fn signature_help_for_tuple_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ expr: ast::TupleExpr,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let active_parameter = Some(
+ expr.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
+ .count(),
+ );
+
+ let db = sema.db;
+ let mut res = SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter,
+ };
+ let expr = sema.type_of_expr(&expr.into())?;
+ let fields = expr.original.tuple_fields(db);
let mut buf = String::new();
- for ty in fields.into_iter().map(|it| it.ty(db)) {
+ for ty in fields {
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
res.push_call_param(&buf);
buf.clear();
}
- res.signature.push_str(")");
+ res.signature.push(')');
Some(res)
}
@@ -465,8 +524,8 @@ fn signature_help_for_record_(
token: SyntaxToken,
) -> Option<SignatureHelp> {
let active_parameter = field_list_children
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,])
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
.take_while(|t| t.text_range().start() <= token.text_range().start())
.count();
@@ -486,7 +545,12 @@ fn signature_help_for_record_(
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} {{ ",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
} else {
let adt = match path_res {
PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
@@ -498,12 +562,12 @@ fn signature_help_for_record_(
hir::Adt::Struct(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} {{ ", it.name(db));
+ format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
}
hir::Adt::Union(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {} {{ ", it.name(db));
+ format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
}
_ => return None,
}
@@ -514,7 +578,7 @@ fn signature_help_for_record_(
let mut buf = String::new();
for (field, ty) in fields2 {
let name = field.name(db);
- format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
@@ -524,7 +588,7 @@ fn signature_help_for_record_(
}
for (name, field) in fields {
let Some(field) = field else { continue };
- format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
}
@@ -532,6 +596,46 @@ fn signature_help_for_record_(
Some(res)
}
+fn signature_help_for_tuple_pat_ish(
+ db: &RootDatabase,
+ mut res: SignatureHelp,
+ pat: &SyntaxNode,
+ token: SyntaxToken,
+ mut field_pats: AstChildren<ast::Pat>,
+ fields: impl ExactSizeIterator<Item = hir::Type>,
+) -> SignatureHelp {
+ let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
+ let is_left_of_rest_pat =
+ rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
+
+ let commas = pat
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,]);
+
+ res.active_parameter = {
+ Some(if is_left_of_rest_pat {
+ commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
+ } else {
+ let n_commas = commas
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ .take_while(|t| t.text_range().start() > token.text_range().start())
+ .count();
+ fields.len().saturating_sub(1).saturating_sub(n_commas)
+ })
+ };
+
+ let mut buf = String::new();
+ for ty in fields {
+ format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+ res.push_call_param(&buf);
+ buf.clear();
+ }
+ res.signature.push_str(")");
+ res
+}
#[cfg(test)]
mod tests {
use std::iter;
@@ -1228,6 +1332,24 @@ fn main() {
}
#[test]
+ fn call_info_for_fn_def_over_reference() {
+ check(
+ r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+ let bar = &&&&&foo;
+ bar($0);
+}
+ "#,
+ expect![[r#"
+ fn foo(s: S) -> i32
+ ^^^^
+ "#]],
+ )
+ }
+
+ #[test]
fn call_info_for_fn_ptr() {
check(
r#"
@@ -1823,4 +1945,290 @@ fn main() {
"#]],
);
}
+
+ #[test]
+ fn test_tuple_expr_free() {
+ check(
+ r#"
+fn main() {
+ (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_expr_expected() {
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ // FIXME: Should typeck report a 4-ary tuple for the expression here?
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32, u32) = ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3, 5);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32, i32)
+ ^^^^ --- --- ---
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_free() {
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ ({unknown}, i32, i32)
+ ^^^^^^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0);
+}
+"#,
+ // FIXME: This is wrong, this should not mark the last as active
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_expected() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0): (i32,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..): (i32, i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
+ #[test]
+ fn test_tuple_pat_expected_inferred() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3) = (1, 2 ,3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3) = (1, 2, 3);
+}
+"#,
+ // FIXME: Should typeck report a 3-ary tuple for the pattern here?
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0) = (1,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..) = (1, 2, 3, 4);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0) = (1, 2, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 497eb1cc1..deaf3c9c4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -56,8 +56,6 @@ pub(crate) fn ssr_assists(
#[cfg(test)]
mod tests {
- use std::sync::Arc;
-
use expect_test::expect;
use ide_assists::{Assist, AssistResolveStrategy};
use ide_db::{
@@ -65,6 +63,7 @@ mod tests {
symbol_index::SymbolsDatabase,
FxHashSet, RootDatabase,
};
+ use triomphe::Arc;
use super::ssr_assists;
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index c97691b14..3e3d9f8f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -118,9 +118,11 @@ impl StaticIndex<'_> {
adjustment_hints_hide_outside_unsafe: false,
hide_named_constructor_hints: false,
hide_closure_initialization_hints: false,
+ closure_style: hir::ClosureStyle::ImplFn,
param_names_for_lifetime_elision_hints: false,
binding_mode_hints: false,
max_length: Some(25),
+ closure_capture_hints: false,
closing_brace_hints_min_lines: Some(25),
},
file_id,
@@ -136,10 +138,10 @@ impl StaticIndex<'_> {
});
let hover_config = HoverConfig {
links_in_hover: true,
+ memory_layout: None,
documentation: true,
keywords: true,
format: crate::HoverDocFormat::Markdown,
- interpret_tests: false,
};
let tokens = tokens.filter(|token| {
matches!(
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 7ce782f93..d2c77e2dc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -1,9 +1,18 @@
-use std::{fmt, sync::Arc};
+use std::{fmt, marker::PhantomData};
-use hir::{ExpandResult, MacroFile};
-use ide_db::base_db::{
- salsa::debug::{DebugQueryTable, TableEntry},
- CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
+use hir::{
+ db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery},
+ Attr, Attrs, ExpandResult, MacroFile, Module,
+};
+use ide_db::{
+ base_db::{
+ salsa::{
+ debug::{DebugQueryTable, TableEntry},
+ Query, QueryTable,
+ },
+ CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId,
+ },
+ symbol_index::ModuleSymbolsQuery,
};
use ide_db::{
symbol_index::{LibrarySymbolsQuery, SymbolIndex},
@@ -14,13 +23,7 @@ use profile::{memory_usage, Bytes};
use std::env;
use stdx::format_to;
use syntax::{ast, Parse, SyntaxNode};
-
-fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
- ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
-fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
- hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
+use triomphe::Arc;
// Feature: Status
//
@@ -34,15 +37,22 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
let mut buf = String::new();
- format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
- format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>());
- format_to!(buf, "{}\n", syntax_tree_stats(db));
- format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db));
+
+ format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db)));
format_to!(buf, "{} in total\n", memory_usage());
if env::var("RA_COUNT").is_ok() {
format_to!(buf, "\nCounts:\n{}", profile::countme::get_all());
}
+ format_to!(buf, "\nDebug info:\n");
+ format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db)));
+ format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db)));
+ format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db)));
+
if let Some(file_id) = file_id {
format_to!(buf, "\nFile info:\n");
let crates = crate::parent_module::crates_for(db, file_id);
@@ -52,8 +62,8 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
let crate_graph = db.crate_graph();
for krate in crates {
let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
- Some(it) => format!("{it}({krate:?})"),
- None => format!("{krate:?}"),
+ Some(it) => format!("{it}({})", krate.into_raw()),
+ None => format!("{}", krate.into_raw()),
};
format_to!(buf, "Crate: {}\n", display_crate(krate));
let deps = crate_graph[krate]
@@ -68,6 +78,82 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
buf.trim().to_string()
}
+fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector
+where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: QueryCollect,
+ <Q as Query>::Storage: 'q,
+ <Q as QueryCollect>::Collector: StatCollect<
+ <QueryTable<'q, Q> as DebugQueryTable>::Key,
+ <QueryTable<'q, Q> as DebugQueryTable>::Value,
+ >,
+{
+ struct StatCollectorWrapper<C>(C);
+ impl<C: StatCollect<K, V>, K, V> FromIterator<TableEntry<K, V>> for StatCollectorWrapper<C> {
+ fn from_iter<T>(iter: T) -> StatCollectorWrapper<C>
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ let mut res = C::default();
+ for entry in iter {
+ res.collect_entry(entry.key, entry.value);
+ }
+ StatCollectorWrapper(res)
+ }
+ }
+ table.entries::<StatCollectorWrapper<<Q as QueryCollect>::Collector>>().0
+}
+
+fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize
+where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: Query,
+ <Q as Query>::Storage: 'q,
+{
+ struct EntryCounter(usize);
+ impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+ fn from_iter<T>(iter: T) -> EntryCounter
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ EntryCounter(iter.into_iter().count())
+ }
+ }
+ table.entries::<EntryCounter>().0
+}
+
+trait QueryCollect: Query {
+ type Collector;
+}
+
+impl QueryCollect for LibrarySymbolsQuery {
+ type Collector = SymbolsStats<SourceRootId>;
+}
+
+impl QueryCollect for ParseQuery {
+ type Collector = SyntaxTreeStats<false>;
+}
+
+impl QueryCollect for ParseMacroExpansionQuery {
+ type Collector = SyntaxTreeStats<true>;
+}
+
+impl QueryCollect for FileTextQuery {
+ type Collector = FilesStats;
+}
+
+impl QueryCollect for ModuleSymbolsQuery {
+ type Collector = SymbolsStats<Module>;
+}
+
+impl QueryCollect for AttrsQuery {
+ type Collector = AttrsStats;
+}
+
+trait StatCollect<K, V>: Default {
+ fn collect_entry(&mut self, key: K, value: Option<V>);
+}
+
#[derive(Default)]
struct FilesStats {
total: usize,
@@ -80,85 +166,98 @@ impl fmt::Display for FilesStats {
}
}
-impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats {
- fn from_iter<T>(iter: T) -> FilesStats
- where
- T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>,
- {
- let mut res = FilesStats::default();
- for entry in iter {
- res.total += 1;
- res.size += entry.value.unwrap().len();
- }
- res
+impl StatCollect<FileId, Arc<str>> for FilesStats {
+ fn collect_entry(&mut self, _: FileId, value: Option<Arc<str>>) {
+ self.total += 1;
+ self.size += value.unwrap().len();
}
}
#[derive(Default)]
-pub(crate) struct SyntaxTreeStats {
+pub(crate) struct SyntaxTreeStats<const MACROS: bool> {
total: usize,
pub(crate) retained: usize,
}
-impl fmt::Display for SyntaxTreeStats {
+impl<const MACROS: bool> fmt::Display for SyntaxTreeStats<MACROS> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(fmt, "{} trees, {} preserved", self.total, self.retained)
+ write!(
+ fmt,
+ "{} trees, {} preserved{}",
+ self.total,
+ self.retained,
+ if MACROS { " (macros)" } else { "" }
+ )
}
}
-impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats {
- fn from_iter<T>(iter: T) -> SyntaxTreeStats
- where
- T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>,
- {
- let mut res = SyntaxTreeStats::default();
- for entry in iter {
- res.total += 1;
- res.retained += entry.value.is_some() as usize;
- }
- res
+impl StatCollect<FileId, Parse<ast::SourceFile>> for SyntaxTreeStats<false> {
+ fn collect_entry(&mut self, _: FileId, value: Option<Parse<ast::SourceFile>>) {
+ self.total += 1;
+ self.retained += value.is_some() as usize;
}
}
-impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
- for SyntaxTreeStats
-{
- fn from_iter<T>(iter: T) -> SyntaxTreeStats
- where
- T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
- {
- let mut res = SyntaxTreeStats::default();
- for entry in iter {
- res.total += 1;
- res.retained += entry.value.is_some() as usize;
+impl<M> StatCollect<MacroFile, ExpandResult<(Parse<SyntaxNode>, M)>> for SyntaxTreeStats<true> {
+ fn collect_entry(&mut self, _: MacroFile, value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>) {
+ self.total += 1;
+ self.retained += value.is_some() as usize;
+ }
+}
+
+struct SymbolsStats<Key> {
+ total: usize,
+ size: Bytes,
+ phantom: PhantomData<Key>,
+}
+
+impl<Key> Default for SymbolsStats<Key> {
+ fn default() -> Self {
+ Self { total: Default::default(), size: Default::default(), phantom: PhantomData }
+ }
+}
+
+impl fmt::Display for SymbolsStats<Module> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{} of module index symbols ({})", self.size, self.total)
+ }
+}
+impl fmt::Display for SymbolsStats<SourceRootId> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{} of library index symbols ({})", self.size, self.total)
+ }
+}
+impl<Key> StatCollect<Key, Arc<SymbolIndex>> for SymbolsStats<Key> {
+ fn collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>) {
+ if let Some(symbols) = value {
+ self.total += symbols.len();
+ self.size += symbols.memory_size();
}
- res
}
}
#[derive(Default)]
-struct LibrarySymbolsStats {
+struct AttrsStats {
+ entries: usize,
total: usize,
- size: Bytes,
}
-impl fmt::Display for LibrarySymbolsStats {
+impl fmt::Display for AttrsStats {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(fmt, "{} of index symbols ({})", self.size, self.total)
+ let size =
+ self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>();
+ let size = Bytes::new(size as _);
+ write!(
+ fmt,
+ "{} attribute query entries, {} total attributes ({} for storing entries)",
+ self.entries, self.total, size
+ )
}
}
-impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats {
- fn from_iter<T>(iter: T) -> LibrarySymbolsStats
- where
- T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>,
- {
- let mut res = LibrarySymbolsStats::default();
- for entry in iter {
- let symbols = entry.value.unwrap();
- res.total += symbols.len();
- res.size += symbols.memory_size();
- }
- res
+impl<Key> StatCollect<Key, Attrs> for AttrsStats {
+ fn collect_entry(&mut self, _: Key, value: Option<Attrs>) {
+ self.entries += 1;
+ self.total += value.map_or(0, |it| it.len());
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 454a250f3..dc06591ff 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -16,13 +16,19 @@ mod tests;
use hir::{Name, Semantics};
use ide_db::{FxHashMap, RootDatabase, SymbolKind};
use syntax::{
- ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
+ ast::{self, IsString},
+ AstNode, AstToken, NodeOrToken,
+ SyntaxKind::*,
+ SyntaxNode, TextRange, WalkEvent, T,
};
use crate::{
syntax_highlighting::{
- escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
- macro_::MacroHighlighter, tags::Highlight,
+ escape::{highlight_escape_char, highlight_escape_string},
+ format::highlight_format_string,
+ highlights::Highlights,
+ macro_::MacroHighlighter,
+ tags::Highlight,
},
FileId, HlMod, HlOperator, HlPunct, HlTag,
};
@@ -163,6 +169,7 @@ pub struct HighlightConfig {
// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
// intraDocLink:: Emitted for intra doc links in doc-strings.
// library:: Emitted for items that are defined outside of the current crate.
+// macro:: Emitted for tokens inside macro calls.
// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
// public:: Emitted for items that are from the current crate and are `pub`.
// reference:: Emitted for locals behind a reference and functions taking `self` by reference.
@@ -236,7 +243,11 @@ fn traverse(
let mut attr_or_derive_item = None;
let mut current_macro: Option<ast::Macro> = None;
let mut macro_highlighter = MacroHighlighter::default();
+
+ // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree
+ // an an attribute nested in a macro call will not emit `inside_attribute`
let mut inside_attribute = false;
+ let mut inside_macro_call = false;
// Walk all nodes, keeping track of whether we are inside a macro or not.
// If in macro, expand it first and highlight the expanded code.
@@ -267,46 +278,50 @@ fn traverse(
inside_attribute = false
}
- Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
- match ast::Item::cast(node.clone()) {
- Some(ast::Item::MacroRules(mac)) => {
- macro_highlighter.init();
- current_macro = Some(mac.into());
- continue;
- }
- Some(ast::Item::MacroDef(mac)) => {
- macro_highlighter.init();
- current_macro = Some(mac.into());
- continue;
- }
- Some(item) => {
- if matches!(node.kind(), FN | CONST | STATIC) {
- bindings_shadow_count.clear();
+ Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) {
+ Some(item) => {
+ match item {
+ ast::Item::MacroRules(mac) => {
+ macro_highlighter.init();
+ current_macro = Some(mac.into());
+ continue;
+ }
+ ast::Item::MacroDef(mac) => {
+ macro_highlighter.init();
+ current_macro = Some(mac.into());
+ continue;
+ }
+ ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
+ bindings_shadow_count.clear()
}
+ ast::Item::MacroCall(_) => {
+ inside_macro_call = true;
+ }
+ _ => (),
+ }
- if attr_or_derive_item.is_none() {
- if sema.is_attr_macro_call(&item) {
- attr_or_derive_item = Some(AttrOrDerive::Attr(item));
- } else {
- let adt = match item {
- ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
- ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
- ast::Item::Union(it) => Some(ast::Adt::Union(it)),
- _ => None,
- };
- match adt {
- Some(adt) if sema.is_derive_annotated(&adt) => {
- attr_or_derive_item =
- Some(AttrOrDerive::Derive(ast::Item::from(adt)));
- }
- _ => (),
+ if attr_or_derive_item.is_none() {
+ if sema.is_attr_macro_call(&item) {
+ attr_or_derive_item = Some(AttrOrDerive::Attr(item));
+ } else {
+ let adt = match item {
+ ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
+ ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
+ ast::Item::Union(it) => Some(ast::Adt::Union(it)),
+ _ => None,
+ };
+ match adt {
+ Some(adt) if sema.is_derive_annotated(&adt) => {
+ attr_or_derive_item =
+ Some(AttrOrDerive::Derive(ast::Item::from(adt)));
}
+ _ => (),
}
}
}
- _ => (),
}
- }
+ _ => (),
+ },
Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
match ast::Item::cast(node.clone()) {
Some(ast::Item::MacroRules(mac)) => {
@@ -324,6 +339,9 @@ fn traverse(
{
attr_or_derive_item = None;
}
+ Some(ast::Item::MacroCall(_)) => {
+ inside_macro_call = false;
+ }
_ => (),
}
}
@@ -419,14 +437,35 @@ fn traverse(
continue;
}
highlight_format_string(hl, &string, &expanded_string, range);
- highlight_escape_string(hl, &string, range.start());
+
+ if !string.is_raw() {
+ highlight_escape_string(hl, &string, range.start());
+ }
}
} else if ast::ByteString::can_cast(token.kind())
&& ast::ByteString::can_cast(descended_token.kind())
{
if let Some(byte_string) = ast::ByteString::cast(token) {
- highlight_escape_string(hl, &byte_string, range.start());
+ if !byte_string.is_raw() {
+ highlight_escape_string(hl, &byte_string, range.start());
+ }
}
+ } else if ast::CString::can_cast(token.kind())
+ && ast::CString::can_cast(descended_token.kind())
+ {
+ if let Some(c_string) = ast::CString::cast(token) {
+ if !c_string.is_raw() {
+ highlight_escape_string(hl, &c_string, range.start());
+ }
+ }
+ } else if ast::Char::can_cast(token.kind())
+ && ast::Char::can_cast(descended_token.kind())
+ {
+ let Some(char) = ast::Char::cast(token) else {
+ continue;
+ };
+
+ highlight_escape_char(hl, &char, range.start())
}
}
@@ -455,32 +494,42 @@ fn traverse(
}
// apply config filtering
- match &mut highlight.tag {
- HlTag::StringLiteral if !config.strings => continue,
- // If punctuation is disabled, make the macro bang part of the macro call again.
- tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
- if !config.macro_bang {
- *tag = HlTag::Symbol(SymbolKind::Macro);
- } else if !config.specialize_punctuation {
- *tag = HlTag::Punctuation(HlPunct::Other);
- }
- }
- HlTag::Punctuation(_) if !config.punctuation => continue,
- tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
- *tag = HlTag::Punctuation(HlPunct::Other);
- }
- HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
- tag @ HlTag::Operator(_) if !config.specialize_operator => {
- *tag = HlTag::Operator(HlOperator::Other);
- }
- _ => (),
+ if !filter_by_config(&mut highlight, config) {
+ continue;
}
if inside_attribute {
highlight |= HlMod::Attribute
}
+ if inside_macro_call && tt_level > 0 {
+ highlight |= HlMod::Macro
+ }
hl.add(HlRange { range, highlight, binding_hash });
}
}
}
+
+fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool {
+ match &mut highlight.tag {
+ HlTag::StringLiteral if !config.strings => return false,
+ // If punctuation is disabled, make the macro bang part of the macro call again.
+ tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
+ if !config.macro_bang {
+ *tag = HlTag::Symbol(SymbolKind::Macro);
+ } else if !config.specialize_punctuation {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ }
+ HlTag::Punctuation(_) if !config.punctuation => return false,
+ tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => return false,
+ tag @ HlTag::Operator(_) if !config.specialize_operator => {
+ *tag = HlTag::Operator(HlOperator::Other);
+ }
+ _ => (),
+ }
+ true
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
index 6a1236c79..211e35880 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
@@ -1,8 +1,8 @@
//! Syntax highlighting for escape sequences
use crate::syntax_highlighting::highlights::Highlights;
use crate::{HlRange, HlTag};
-use syntax::ast::IsString;
-use syntax::TextSize;
+use syntax::ast::{Char, IsString};
+use syntax::{AstToken, TextRange, TextSize};
pub(super) fn highlight_escape_string<T: IsString>(
stack: &mut Highlights,
@@ -23,3 +23,23 @@ pub(super) fn highlight_escape_string<T: IsString>(
}
});
}
+
+pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
+ if char.value().is_none() {
+ return;
+ }
+
+ let text = char.text();
+ if !text.starts_with('\'') || !text.ends_with('\'') {
+ return;
+ }
+
+ let text = &text[1..text.len() - 1];
+ if !text.starts_with('\\') {
+ return;
+ }
+
+ let range =
+ TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1));
+ stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 2111baad7..3c40246a6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -26,7 +26,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O
}
let highlight: Highlight = match token.kind() {
- STRING | BYTE_STRING => HlTag::StringLiteral.into(),
+ STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(),
INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
SymbolKind::Field.into()
}
@@ -340,7 +340,7 @@ fn highlight_def(
Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)),
Definition::Module(module) => {
let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
- if module.is_crate_root(db) {
+ if module.is_crate_root() {
h |= HlMod::CrateRoot;
}
h
@@ -675,14 +675,12 @@ fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase)
/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
- while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
+ while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
if parent.kind() != *kind {
return false;
}
- // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
- // in the same pattern is unstable: rust-lang/rust#68354.
- node = node.parent().unwrap().into();
+ node = parent.into();
kinds = rest;
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 3c4cfc781..901df147d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -52,7 +52,11 @@ pub(super) fn ra_fixture(
if let Some(next) = text.strip_prefix(marker) {
if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
- hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None });
+ hl.add(HlRange {
+ range,
+ highlight: HlTag::Keyword | HlMod::Injected,
+ binding_hash: None,
+ });
}
text = next;
@@ -66,7 +70,16 @@ pub(super) fn ra_fixture(
for mut hl_range in analysis
.highlight(
- HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
+ HighlightConfig {
+ syntactic_name_ref_highlighting: false,
+ punctuation: true,
+ operator: true,
+ strings: true,
+ specialize_punctuation: config.specialize_punctuation,
+ specialize_operator: config.operator,
+ inject_doc_comment: config.inject_doc_comment,
+ macro_bang: config.macro_bang,
+ },
tmp_file_id,
)
.unwrap()
@@ -74,6 +87,7 @@ pub(super) fn ra_fixture(
for range in inj.map_range_up(hl_range.range) {
if let Some(range) = literal.map_range_up(range) {
hl_range.range = range;
+ hl_range.highlight |= HlMod::Injected;
hl.add(hl_range);
}
}
@@ -217,7 +231,16 @@ pub(super) fn doc_comment(
if let Ok(ranges) = analysis.with_db(|db| {
super::highlight(
db,
- HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
+ HighlightConfig {
+ syntactic_name_ref_highlighting: true,
+ punctuation: true,
+ operator: true,
+ strings: true,
+ specialize_punctuation: config.specialize_punctuation,
+ specialize_operator: config.operator,
+ inject_doc_comment: config.inject_doc_comment,
+ macro_bang: config.macro_bang,
+ },
tmp_file_id,
None,
)
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index a81c4ee0c..f98310911 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -49,7 +49,7 @@ pub enum HlMod {
Associated = 0,
/// Used with keywords like `async` and `await`.
Async,
- /// Used to differentiate individual elements within attributes.
+ /// Used to differentiate individual elements within attribute calls.
Attribute,
/// Callable item or value.
Callable,
@@ -72,6 +72,8 @@ pub enum HlMod {
IntraDocLink,
/// Used for items from other crates.
Library,
+ /// Used to differentiate individual elements within macro calls.
+ Macro,
/// Mutable binding.
Mutable,
/// Used for public items.
@@ -200,7 +202,7 @@ impl fmt::Display for HlTag {
}
impl HlMod {
- const ALL: &'static [HlMod; 19] = &[
+ const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
HlMod::Associated,
HlMod::Async,
HlMod::Attribute,
@@ -214,6 +216,7 @@ impl HlMod {
HlMod::Injected,
HlMod::IntraDocLink,
HlMod::Library,
+ HlMod::Macro,
HlMod::Mutable,
HlMod::Public,
HlMod::Reference,
@@ -237,6 +240,7 @@ impl HlMod {
HlMod::Injected => "injected",
HlMod::IntraDocLink => "intra_doc_link",
HlMod::Library => "library",
+ HlMod::Macro => "macro",
HlMod::Mutable => "mutable",
HlMod::Public => "public",
HlMod::Reference => "reference",
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 18045f1f5..35f240d42 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -93,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="comment injected">// calls bar on foo</span>
- <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+ <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="none injected macro">foo</span><span class="operator injected macro">.</span><span class="none injected macro">bar</span><span class="parenthesis injected macro">(</span><span class="parenthesis injected macro">)</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
@@ -145,7 +145,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
@@ -165,7 +165,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected macro">[</span><span class="numeric_literal injected macro">1</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">2</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">3</span><span class="bracket injected macro">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index af41796e2..87b9da46e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -43,5 +43,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
-<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration library">abc</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root default_library declaration library">abc</span><span class="semicolon">;</span>
</code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9f2b1926b..6b049f379 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -178,7 +178,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
<span class="keyword">fn</span> <span class="function associated consuming declaration">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
<span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
- <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
+ <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma">,</span>
<span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -192,7 +192,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">f1</span><span class="comma macro">,</span> <span class="none macro">f2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">use_foo_items</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -204,7 +204,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">control_flow</span> <span class="operator">=</span> <span class="module crate_root library">foo</span><span class="operator">::</span><span class="function library">identity</span><span class="parenthesis">(</span><span class="module crate_root library">foo</span><span class="operator">::</span><span class="enum library">ControlFlow</span><span class="operator">::</span><span class="enum_variant library">Continue</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword control">if</span> <span class="variable">control_flow</span><span class="operator">.</span><span class="function associated consuming library">should_die</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index abcd80c28..d9c3db6fb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -45,18 +45,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
-<span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration static trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="brace">}</span>
-<span class="brace">}</span><span class="string_literal">"#</span>
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
+</span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="string_literal injected macro">"2 + 2 = {}"</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">4</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span><span class="none injected">
+ </span><span class="brace injected">}</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"#</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
-<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span>
- <span class="numeric_literal">92</span>
- <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span>
-<span class="brace">}</span><span class="string_literal">"</span>
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span><span class="none injected">
+</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="numeric_literal injected">92</span><span class="none injected">
+ </span><span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index 66f9ede96..3900959be 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -53,6 +53,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 54d427952..2cbbf6964 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -42,21 +42,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
-<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="brace">{</span>
- <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">x</span> <span class="keyword">pub</span>
- <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">y</span> <span class="keyword">pub</span>
- <span class="brace">}</span> <span class="struct declaration">Foo</span> <span class="keyword">struct</span>
-<span class="brace">}</span>
+<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="brace macro">{</span>
+ <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">x</span> <span class="keyword macro">pub</span>
+ <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">y</span> <span class="keyword macro">pub</span>
+ <span class="brace macro">}</span> <span class="struct declaration macro">Foo</span> <span class="keyword macro">struct</span>
+<span class="brace macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
<span class="brace">}</span>
-<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
- <span class="numeric_literal">100</span>
- <span class="brace">}</span>
-<span class="brace">}</span>
+<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
+ <span class="numeric_literal macro">100</span>
+ <span class="brace macro">}</span>
+<span class="brace macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
@@ -90,7 +90,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, {}!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index a626cda3f..fa374b04f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -86,6 +86,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">asm</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">concat</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -93,72 +95,84 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'\e'</span><span class="semicolon">;</span> <span class="comment">// invalid escape</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'e'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">' '</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{48}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{4823}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x65</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x00</span><span class="char_literal">'</span><span class="semicolon">;</span>
+
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// escape sequences</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> <span class="unresolved_reference">thingy</span><span class="comma">,</span> <span class="unresolved_reference">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> asdasd"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">fmt"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span>concat<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="comma">,</span> <span class="string_literal">"{}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="operator macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 1992bdc6a..654d51b8a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -89,13 +89,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
- <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span>
- <span class="brace">}</span><span class="semicolon">;</span>
+ <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
+ <span class="brace macro">}</span><span class="semicolon">;</span>
<span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
- <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span>
+ <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span><span class="semicolon">;</span>
<span class="comment">// unsafe fn and method calls</span>
<span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index ac9bd8e39..497992f68 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -432,6 +432,8 @@ macro_rules! panic {}
macro_rules! assert {}
#[rustc_builtin_macro]
macro_rules! asm {}
+#[rustc_builtin_macro]
+macro_rules! concat {}
macro_rules! toho {
() => ($crate::panic!("not yet implemented"));
@@ -439,6 +441,16 @@ macro_rules! toho {
}
fn main() {
+ let a = '\n';
+ let a = '\t';
+ let a = '\e'; // invalid escape
+ let a = 'e';
+ let a = ' ';
+ let a = '\u{48}';
+ let a = '\u{4823}';
+ let a = '\x65';
+ let a = '\x00';
+
println!("Hello {{Hello}}");
// from https://doc.rust-lang.org/std/fmt/index.html
println!("Hello"); // => "Hello"
@@ -495,6 +507,7 @@ fn main() {
let _ = "\x28\x28\x00\x63\n";
let _ = b"\x28\x28\x00\x63\n";
+ let _ = r"\\";
println!("{\x41}", A = 92);
println!("{ничоси}", ничоси = 92);
@@ -507,6 +520,7 @@ fn main() {
toho!("{}fmt", 0);
asm!("mov eax, {0}");
format_args!(concat!("{}"), "{}");
+ format_args!("{}", format_args!("{}", 0));
}"#,
expect_file!["./test_data/highlight_strings.html"],
false,
@@ -1126,5 +1140,5 @@ fn benchmark_syntax_highlighting_parser() {
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count()
};
- assert_eq!(hash, 1608);
+ assert_eq!(hash, 1169);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index bb6827e8a..df1971242 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -1,5 +1,7 @@
-use ide_db::base_db::{FileId, SourceDatabase};
-use ide_db::RootDatabase;
+use ide_db::{
+ base_db::{FileId, SourceDatabase},
+ RootDatabase,
+};
use syntax::{
AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
};
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 17a1e385b..8c84461f6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -1,11 +1,9 @@
-use std::sync::Arc;
-
use dot::{Id, LabelText};
use ide_db::{
base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
- RootDatabase,
+ FxHashSet, RootDatabase,
};
-use stdx::hash::NoHashHashSet;
+use triomphe::Arc;
// Feature: View Crate Graph
//
@@ -42,7 +40,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
struct DotCrateGraph {
graph: Arc<CrateGraph>,
- crates_to_render: NoHashHashSet<CrateId>,
+ crates_to_render: FxHashSet<CrateId>,
}
type Edge<'a> = (CrateId, &'a Dependency);
@@ -80,7 +78,7 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
}
fn node_id(&'a self, n: &CrateId) -> Id<'a> {
- Id::new(format!("_{}", n.0)).unwrap()
+ Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap()
}
fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index 9c1f93356..e072df430 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -12,5 +12,5 @@ use ide_db::RootDatabase;
// | VS Code | **rust-analyzer: Debug ItemTree**
// |===
pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
- db.file_item_tree(file_id.into()).pretty_print()
+ db.file_item_tree(file_id.into()).pretty_print(db)
}
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index c73c368a1..dcd0d7881 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -18,3 +18,4 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] }
hashbrown = { version = "0.12.1", default-features = false }
once_cell = "1.17.0"
rustc-hash = "1.1.0"
+triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index fb2903696..dabbf3a38 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -6,13 +6,13 @@ use std::{
fmt::{self, Debug, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
- sync::Arc,
};
use dashmap::{DashMap, SharedValue};
-use hashbrown::HashMap;
+use hashbrown::{hash_map::RawEntryMut, HashMap};
use once_cell::sync::OnceCell;
use rustc_hash::FxHasher;
+use triomphe::Arc;
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
type Guard<T> = dashmap::RwLockWriteGuard<
@@ -26,56 +26,58 @@ pub struct Interned<T: Internable + ?Sized> {
impl<T: Internable> Interned<T> {
pub fn new(obj: T) -> Self {
- match Interned::lookup(&obj) {
- Ok(this) => this,
- Err(shard) => {
- let arc = Arc::new(obj);
- Self::alloc(arc, shard)
- }
+ let (mut shard, hash) = Self::select(&obj);
+ // Atomically,
+ // - check if `obj` is already in the map
+ // - if so, clone its `Arc` and return it
+ // - if not, box it up, insert it, and return a clone
+ // This needs to be atomic (locking the shard) to avoid races with other thread, which could
+ // insert the same object between us looking it up and inserting it.
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) {
+ RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() },
+ RawEntryMut::Vacant(vac) => Self {
+ arc: vac
+ .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(()))
+ .0
+ .clone(),
+ },
}
}
}
-impl<T: Internable + ?Sized> Interned<T> {
- fn lookup(obj: &T) -> Result<Self, Guard<T>> {
- let storage = T::storage().get();
- let shard_idx = storage.determine_map(obj);
- let shard = &storage.shards()[shard_idx];
- let shard = shard.write();
-
+impl Interned<str> {
+ pub fn new_str(s: &str) -> Self {
+ let (mut shard, hash) = Self::select(s);
// Atomically,
// - check if `obj` is already in the map
// - if so, clone its `Arc` and return it
// - if not, box it up, insert it, and return a clone
// This needs to be atomic (locking the shard) to avoid races with other thread, which could
// insert the same object between us looking it up and inserting it.
-
- // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when
- // hashbrown can be plugged into dashmap)
- match shard.get_key_value(obj) {
- Some((arc, _)) => Ok(Self { arc: arc.clone() }),
- None => Err(shard),
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) {
+ RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() },
+ RawEntryMut::Vacant(vac) => Self {
+ arc: vac
+ .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(()))
+ .0
+ .clone(),
+ },
}
}
-
- fn alloc(arc: Arc<T>, mut shard: Guard<T>) -> Self {
- let arc2 = arc.clone();
-
- shard.insert(arc2, SharedValue::new(()));
-
- Self { arc }
- }
}
-impl Interned<str> {
- pub fn new_str(s: &str) -> Self {
- match Interned::lookup(s) {
- Ok(this) => this,
- Err(shard) => {
- let arc = Arc::<str>::from(s);
- Self::alloc(arc, shard)
- }
- }
+impl<T: Internable + ?Sized> Interned<T> {
+ #[inline]
+ fn select(obj: &T) -> (Guard<T>, u64) {
+ let storage = T::storage().get();
+ let hash = {
+ let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher());
+ obj.hash(&mut hasher);
+ hasher.finish()
+ };
+ let shard_idx = storage.determine_shard(hash as usize);
+ let shard = &storage.shards()[shard_idx];
+ (shard.write(), hash)
}
}
@@ -83,7 +85,7 @@ impl<T: Internable + ?Sized> Drop for Interned<T> {
#[inline]
fn drop(&mut self) {
// When the last `Ref` is dropped, remove the object from the global map.
- if Arc::strong_count(&self.arc) == 2 {
+ if Arc::count(&self.arc) == 2 {
// Only `self` and the global map point to the object.
self.drop_slow();
@@ -94,20 +96,17 @@ impl<T: Internable + ?Sized> Drop for Interned<T> {
impl<T: Internable + ?Sized> Interned<T> {
#[cold]
fn drop_slow(&mut self) {
- let storage = T::storage().get();
- let shard_idx = storage.determine_map(&self.arc);
- let shard = &storage.shards()[shard_idx];
- let mut shard = shard.write();
-
- // FIXME: avoid double lookup
- let (arc, _) = shard.get_key_value(&self.arc).expect("interned value removed prematurely");
+ let (mut shard, hash) = Self::select(&self.arc);
- if Arc::strong_count(arc) != 2 {
+ if Arc::count(&self.arc) != 2 {
// Another thread has interned another copy
return;
}
- shard.remove(&self.arc);
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &self.arc) {
+ RawEntryMut::Occupied(occ) => occ.remove(),
+ RawEntryMut::Vacant(_) => unreachable!(),
+ };
// Shrink the backing storage if the shard is less than 50% occupied.
if shard.len() * 2 < shard.capacity() {
diff --git a/src/tools/rust-analyzer/crates/limit/src/lib.rs b/src/tools/rust-analyzer/crates/limit/src/lib.rs
index 6b2534aa4..7fb4b513a 100644
--- a/src/tools/rust-analyzer/crates/limit/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/limit/src/lib.rs
@@ -6,6 +6,7 @@
use std::sync::atomic::AtomicUsize;
/// Represents a struct used to enforce a numerical limit.
+#[derive(Debug)]
pub struct Limit {
upper_bound: usize,
#[cfg(feature = "tracking")]
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index 894355fcb..d28dd17de 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() {
let rules = macro_rules_fixtures_tt();
let hash: usize = {
let _pt = bench("mbe parse macro rules");
- rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum()
+ rules
+ .values()
+ .map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len())
+ .sum()
};
assert_eq!(hash, 1144);
}
@@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() {
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
macro_rules_fixtures_tt()
.into_iter()
- .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap()))
+ .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap()))
.collect()
}
@@ -76,7 +79,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
let mut res = Vec::new();
for (name, it) in rules {
- for rule in &it.rules {
+ for rule in it.rules.iter() {
// Generate twice
for _ in 0..2 {
// The input are generated by filling the `Op` randomly.
@@ -108,7 +111,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
}
try_cnt += 1;
if try_cnt > 100 {
- panic!("invocaton fixture {name} cannot be generated.\n");
+ panic!("invocation fixture {name} cannot be generated.\n");
}
}
}
@@ -192,10 +195,10 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
});
parent.token_trees.push(subtree.into());
}
- Op::Ignore { .. } | Op::Index { .. } => {}
+ Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {}
};
- // Simple linear congruential generator for determistic result
+ // Simple linear congruential generator for deterministic result
fn rand(seed: &mut usize) -> usize {
let a = 1664525;
let c = 1013904223;
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index 7537dc322..8e2181e97 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult};
pub(crate) fn expand_rules(
rules: &[crate::Rule],
input: &tt::Subtree,
+ is_2021: bool,
) -> ExpandResult<tt::Subtree> {
let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
for rule in rules {
- let new_match = matcher::match_(&rule.lhs, input);
+ let new_match = matcher::match_(&rule.lhs, input, is_2021);
if new_match.err.is_none() {
// If we find a rule that applies without errors, we're done.
@@ -45,7 +46,7 @@ pub(crate) fn expand_rules(
transcriber::transcribe(&rule.rhs, &match_.bindings);
ExpandResult { value, err: match_.err.or(transcribe_err) }
} else {
- ExpandResult::with_err(
+ ExpandResult::new(
tt::Subtree { delimiter: tt::Delimiter::unspecified(), token_trees: vec![] },
ExpandError::NoMatchingRule,
)
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index f4ea9e5c8..474826079 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -111,8 +111,8 @@ impl Match {
}
/// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
- let mut res = match_loop(pattern, input);
+pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match {
+ let mut res = match_loop(pattern, input, is_2021);
res.bound_count = count(res.bindings.bindings());
return res;
@@ -332,7 +332,7 @@ struct MatchState<'t> {
/// Cached result of meta variable parsing
meta_result: Option<(TtIter<'t>, ExpandResult<Option<Fragment>>)>,
- /// Is error occuried in this state, will `poised` to "parent"
+ /// Is error occurred in this state, will `poised` to "parent"
is_error: bool,
}
@@ -354,6 +354,7 @@ struct MatchState<'t> {
/// - `eof_items`: the set of items that would be valid if this was the EOF.
/// - `bb_items`: the set of items that are waiting for the black-box parser.
/// - `error_items`: the set of items in errors, used for error-resilient parsing
+#[inline]
fn match_loop_inner<'t>(
src: TtIter<'t>,
stack: &[TtIter<'t>],
@@ -364,6 +365,7 @@ fn match_loop_inner<'t>(
next_items: &mut Vec<MatchState<'t>>,
eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
error_items: &mut SmallVec<[MatchState<'t>; 1]>,
+ is_2021: bool,
) {
macro_rules! try_push {
($items: expr, $it:expr) => {
@@ -474,7 +476,7 @@ fn match_loop_inner<'t>(
OpDelimited::Op(Op::Var { kind, name, .. }) => {
if let &Some(kind) = kind {
let mut fork = src.clone();
- let match_res = match_meta_var(kind, &mut fork);
+ let match_res = match_meta_var(kind, &mut fork, is_2021);
match match_res.err {
None => {
// Some meta variables are optional (e.g. vis)
@@ -565,7 +567,9 @@ fn match_loop_inner<'t>(
item.is_error = true;
error_items.push(item);
}
- OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {}
+ OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => {
+ stdx::never!("metavariable expression in lhs found");
+ }
OpDelimited::Open => {
if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) {
item.dot.next();
@@ -583,7 +587,7 @@ fn match_loop_inner<'t>(
}
}
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
+fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match {
let mut src = TtIter::new(src);
let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
let mut res = Match::default();
@@ -622,6 +626,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
&mut next_items,
&mut eof_items,
&mut error_items,
+ is_2021,
);
stdx::always!(cur_items.is_empty());
@@ -731,14 +736,17 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
}
}
-fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
+fn match_meta_var(
+ kind: MetaVarKind,
+ input: &mut TtIter<'_>,
+ is_2021: bool,
+) -> ExpandResult<Option<Fragment>> {
let fragment = match kind {
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
- // FIXME: These two should actually behave differently depending on the edition.
- //
- // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
- MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
+ MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
+ MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
+ MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
@@ -805,7 +813,9 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate)
Op::Var { name, .. } => collector_fun(name.clone()),
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
- Op::Ignore { .. } | Op::Index { .. } | Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {
+ Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
+ Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {
+ stdx::never!("metavariable expression in lhs found");
}
}
}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index dffb40d4b..6161af185 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -7,7 +7,7 @@ use crate::{
expander::{Binding, Bindings, Fragment},
parser::{MetaVarKind, Op, RepeatKind, Separator},
tt::{self, Delimiter},
- ExpandError, ExpandResult, MetaTemplate,
+ CountError, ExpandError, ExpandResult, MetaTemplate,
};
impl Bindings {
@@ -15,13 +15,23 @@ impl Bindings {
self.inner.contains_key(name)
}
- fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<Fragment, ExpandError> {
+ fn get(&self, name: &str) -> Result<&Binding, ExpandError> {
+ match self.inner.get(name) {
+ Some(binding) => Ok(binding),
+ None => Err(ExpandError::binding_error(format!("could not find binding `{name}`"))),
+ }
+ }
+
+ fn get_fragment(
+ &self,
+ name: &str,
+ nesting: &mut [NestingState],
+ ) -> Result<Fragment, ExpandError> {
macro_rules! binding_err {
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
}
- let mut b: &Binding =
- self.inner.get(name).ok_or_else(|| binding_err!("could not find binding `{name}`"))?;
+ let mut b = self.get(name)?;
for nesting_state in nesting.iter_mut() {
nesting_state.hit = true;
b = match b {
@@ -133,7 +143,7 @@ fn expand_subtree(
// remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
let start_elements = arena.len();
let mut err = None;
- for op in template.iter() {
+ 'ops: for op in template.iter() {
match op {
Op::Literal(it) => arena.push(tt::Leaf::from(it.clone()).into()),
Op::Ident(it) => arena.push(tt::Leaf::from(it.clone()).into()),
@@ -161,13 +171,12 @@ fn expand_subtree(
}
Op::Ignore { name, id } => {
// Expand the variable, but ignore the result. This registers the repetition count.
+ // FIXME: Any emitted errors are dropped.
expand_var(ctx, name, *id);
}
Op::Index { depth } => {
- let index = ctx
- .nesting
- .get(ctx.nesting.len() - 1 - (*depth as usize))
- .map_or(0, |nest| nest.idx);
+ let index =
+ ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
arena.push(
tt::Leaf::Literal(tt::Literal {
text: index.to_string().into(),
@@ -176,6 +185,65 @@ fn expand_subtree(
.into(),
);
}
+ Op::Count { name, depth } => {
+ let mut binding = match ctx.bindings.get(name.as_str()) {
+ Ok(b) => b,
+ Err(e) => {
+ if err.is_none() {
+ err = Some(e);
+ }
+ continue;
+ }
+ };
+ for state in ctx.nesting.iter_mut() {
+ state.hit = true;
+ match binding {
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ // `count()` will report an error.
+ break;
+ }
+ Binding::Nested(bs) => {
+ if let Some(b) = bs.get(state.idx) {
+ binding = b;
+ } else {
+ state.at_end = true;
+ continue 'ops;
+ }
+ }
+ Binding::Empty => {
+ state.at_end = true;
+ // FIXME: Breaking here and proceeding to `count()` isn't the most
+ // correct thing to do here. This could be a binding of some named
+ // fragment which we don't know the depth of, so `count()` will just
+ // return 0 for this no matter what `depth` is. See test
+ // `count_interaction_with_empty_binding` for example.
+ break;
+ }
+ }
+ }
+
+ let c = match count(ctx, binding, 0, *depth) {
+ Ok(c) => c,
+ Err(e) => {
+ // XXX: It *might* make sense to emit a dummy integer value like `0` here.
+ // That would type inference a bit more robust in cases like
+ // `v[${count(t)}]` where index doesn't matter, but also coult also lead to
+ // wrong infefrence for cases like `tup.${count(t)}` where index itself
+ // does matter.
+ if err.is_none() {
+ err = Some(e.into());
+ }
+ continue;
+ }
+ };
+ arena.push(
+ tt::Leaf::Literal(tt::Literal {
+ text: c.to_string().into(),
+ span: tt::TokenId::unspecified(),
+ })
+ .into(),
+ );
+ }
}
}
// drain the elements added in this instance of expand_subtree
@@ -218,12 +286,9 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe
.into();
ExpandResult::ok(Fragment::Tokens(tt))
} else {
- ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
+ ctx.bindings.get_fragment(v, &mut ctx.nesting).map_or_else(
|e| ExpandResult {
- value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter::unspecified(),
- token_trees: vec![],
- })),
+ value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty())),
err: Some(e),
},
ExpandResult::ok,
@@ -245,6 +310,7 @@ fn expand_repeat(
let limit = 65536;
let mut has_seps = 0;
let mut counter = 0;
+ let mut err = None;
loop {
let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, None, arena);
@@ -272,6 +338,7 @@ fn expand_repeat(
}
if e.is_some() {
+ err = err.or(e);
continue;
}
@@ -317,7 +384,7 @@ fn expand_repeat(
err: Some(ExpandError::UnexpectedToken),
};
}
- ExpandResult::ok(Fragment::Tokens(tt))
+ ExpandResult { value: Fragment::Tokens(tt), err }
}
fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
@@ -343,3 +410,34 @@ fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
_ => buf.push(tt.into()),
}
}
+
+/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
+/// defined by the metavar expression.
+fn count(
+ ctx: &ExpandCtx<'_>,
+ binding: &Binding,
+ our_depth: usize,
+ count_depth: Option<usize>,
+) -> Result<usize, CountError> {
+ match binding {
+ Binding::Nested(bs) => match count_depth {
+ None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(),
+ Some(0) => Ok(bs.len()),
+ Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(),
+ },
+ Binding::Empty => Ok(0),
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ if our_depth == 0 {
+ // `${count(t)}` is placed inside the innermost repetition. This includes cases
+ // where `t` is not a repeated fragment.
+ Err(CountError::Misplaced)
+ } else if count_depth.is_none() {
+ Ok(1)
+ } else {
+ // We've reached at the innermost repeated fragment, but the user wants us to go
+ // further!
+ Err(CountError::OutOfBounds)
+ }
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index ac107a0d6..5ef20ff8a 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -19,6 +19,7 @@ mod benchmark;
mod token_map;
use ::tt::token_id as tt;
+use stdx::impl_from;
use std::fmt;
@@ -69,7 +70,7 @@ impl fmt::Display for ParseError {
}
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
BindingError(Box<Box<str>>),
LeftoverTokens,
@@ -77,8 +78,11 @@ pub enum ExpandError {
LimitExceeded,
NoMatchingRule,
UnexpectedToken,
+ CountError(CountError),
}
+impl_from!(CountError for ExpandError);
+
impl ExpandError {
fn binding_error(e: impl Into<Box<str>>) -> ExpandError {
ExpandError::BindingError(Box::new(e.into()))
@@ -94,6 +98,23 @@ impl fmt::Display for ExpandError {
ExpandError::ConversionError => f.write_str("could not convert tokens"),
ExpandError::LimitExceeded => f.write_str("Expand exceed limit"),
ExpandError::LeftoverTokens => f.write_str("leftover tokens"),
+ ExpandError::CountError(e) => e.fmt(f),
+ }
+ }
+}
+
+// FIXME: Showing these errors could be nicer.
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub enum CountError {
+ OutOfBounds,
+ Misplaced,
+}
+
+impl fmt::Display for CountError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ CountError::OutOfBounds => f.write_str("${count} out of bounds"),
+ CountError::Misplaced => f.write_str("${count} misplaced"),
}
}
}
@@ -104,9 +125,12 @@ impl fmt::Display for ExpandError {
/// and `$()*` have special meaning (see `Var` and `Repeat` data structures)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeclarativeMacro {
- rules: Vec<Rule>,
+ rules: Box<[Rule]>,
/// Highest id of the token we have in TokenMap
shift: Shift,
+ // This is used for correctly determining the behavior of the pat fragment
+ // FIXME: This should be tracked by hygiene of the fragment identifier!
+ is_2021: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -190,7 +214,10 @@ pub enum Origin {
impl DeclarativeMacro {
/// The old, `macro_rules! m {}` flavor.
- pub fn parse_macro_rules(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
+ pub fn parse_macro_rules(
+ tt: &tt::Subtree,
+ is_2021: bool,
+ ) -> Result<DeclarativeMacro, ParseError> {
// Note: this parsing can be implemented using mbe machinery itself, by
// matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
// manually seems easier.
@@ -211,11 +238,11 @@ impl DeclarativeMacro {
validate(lhs)?;
}
- Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
+ Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 })
}
/// The new, unstable `macro m {}` flavor.
- pub fn parse_macro2(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
+ pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result<DeclarativeMacro, ParseError> {
let mut src = TtIter::new(tt);
let mut rules = Vec::new();
@@ -244,14 +271,14 @@ impl DeclarativeMacro {
validate(lhs)?;
}
- Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
+ Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 })
}
pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
// apply shift
let mut tt = tt.clone();
self.shift.shift_all(&mut tt);
- expander::expand_rules(&self.rules, &tt)
+ expander::expand_rules(&self.rules, &tt, self.is_2021)
}
pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
@@ -324,12 +351,12 @@ pub struct ValueResult<T, E> {
}
impl<T, E> ValueResult<T, E> {
- pub fn ok(value: T) -> Self {
- Self { value, err: None }
+ pub fn new(value: T, err: E) -> Self {
+ Self { value, err: Some(err) }
}
- pub fn with_err(value: T, err: E) -> Self {
- Self { value, err: Some(err) }
+ pub fn ok(value: T) -> Self {
+ Self { value, err: None }
}
pub fn only_err(err: E) -> Self
diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index fd3d64719..7a143e746 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
@@ -20,7 +20,7 @@ use crate::{tt, tt_iter::TtIter, ParseError};
/// Stuff to the right is a [`MetaTemplate`] template which is used to produce
/// output.
#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) struct MetaTemplate(pub(crate) Vec<Op>);
+pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>);
impl MetaTemplate {
pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<MetaTemplate, ParseError> {
@@ -44,7 +44,7 @@ impl MetaTemplate {
res.push(op);
}
- Ok(MetaTemplate(res))
+ Ok(MetaTemplate(res.into_boxed_slice()))
}
}
@@ -52,7 +52,8 @@ impl MetaTemplate {
pub(crate) enum Op {
Var { name: SmolStr, kind: Option<MetaVarKind>, id: tt::TokenId },
Ignore { name: SmolStr, id: tt::TokenId },
- Index { depth: u32 },
+ Index { depth: usize },
+ Count { name: SmolStr, depth: Option<usize> },
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
Subtree { tokens: MetaTemplate, delimiter: tt::Delimiter },
Literal(tt::Literal),
@@ -295,9 +296,13 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result<Op, ()> {
let ident = args.expect_ident()?;
Op::Ignore { name: ident.text.clone(), id: ident.span }
}
- "index" => {
- let depth = if args.len() == 0 { 0 } else { args.expect_u32_literal()? };
- Op::Index { depth }
+ "index" => Op::Index { depth: parse_depth(&mut args)? },
+ "count" => {
+ let ident = args.expect_ident()?;
+ // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug
+ // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904.
+ let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
+ Op::Count { name: ident.text.clone(), depth }
}
_ => return Err(()),
};
@@ -308,3 +313,22 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result<Op, ()> {
Ok(op)
}
+
+fn parse_depth(src: &mut TtIter<'_>) -> Result<usize, ()> {
+ if src.len() == 0 {
+ Ok(0)
+ } else if let tt::Leaf::Literal(lit) = src.expect_literal()? {
+ // Suffixes are not allowed.
+ lit.text.parse().map_err(|_| ())
+ } else {
+ Err(())
+ }
+}
+
+fn try_eat_comma(src: &mut TtIter<'_>) -> bool {
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) {
+ let _ = src.next();
+ return true;
+ }
+ false
+}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index fb5313401..8cbf0f8fc 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -190,20 +190,13 @@ fn convert_tokens<C: TokenConverter>(conv: &mut C) -> tt::Subtree {
let kind = token.kind(conv);
if kind == COMMENT {
- if let Some(tokens) = conv.convert_doc_comment(&token) {
- // FIXME: There has to be a better way to do this
- // Add the comments token id to the converted doc string
+ // Since `convert_doc_comment` can fail, we need to peek the next id, so that we can
+ // figure out which token id to use for the doc comment, if it is converted successfully.
+ let next_id = conv.id_alloc().peek_next_id();
+ if let Some(tokens) = conv.convert_doc_comment(&token, next_id) {
let id = conv.id_alloc().alloc(range, synth_id);
- result.extend(tokens.into_iter().map(|mut tt| {
- if let tt::TokenTree::Subtree(sub) = &mut tt {
- if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) =
- sub.token_trees.get_mut(2)
- {
- lit.span = id
- }
- }
- tt
- }));
+ debug_assert_eq!(id, next_id);
+ result.extend(tokens);
}
continue;
}
@@ -382,49 +375,46 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
text.into()
}
-fn convert_doc_comment(token: &syntax::SyntaxToken) -> Option<Vec<tt::TokenTree>> {
+fn convert_doc_comment(
+ token: &syntax::SyntaxToken,
+ span: tt::TokenId,
+) -> Option<Vec<tt::TokenTree>> {
cov_mark::hit!(test_meta_doc_comments);
let comment = ast::Comment::cast(token.clone())?;
let doc = comment.kind().doc?;
// Make `doc="\" Comments\""
- let meta_tkns = vec![mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)];
+ let meta_tkns =
+ vec![mk_ident("doc", span), mk_punct('=', span), mk_doc_literal(&comment, span)];
// Make `#![]`
let mut token_trees = Vec::with_capacity(3);
- token_trees.push(mk_punct('#'));
+ token_trees.push(mk_punct('#', span));
if let ast::CommentPlacement::Inner = doc {
- token_trees.push(mk_punct('!'));
+ token_trees.push(mk_punct('!', span));
}
token_trees.push(tt::TokenTree::from(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::UNSPECIFIED,
- close: tt::TokenId::UNSPECIFIED,
- kind: tt::DelimiterKind::Bracket,
- },
+ delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
token_trees: meta_tkns,
}));
return Some(token_trees);
// Helper functions
- fn mk_ident(s: &str) -> tt::TokenTree {
- tt::TokenTree::from(tt::Leaf::from(tt::Ident {
- text: s.into(),
- span: tt::TokenId::unspecified(),
- }))
+ fn mk_ident(s: &str, span: tt::TokenId) -> tt::TokenTree {
+ tt::TokenTree::from(tt::Leaf::from(tt::Ident { text: s.into(), span }))
}
- fn mk_punct(c: char) -> tt::TokenTree {
+ fn mk_punct(c: char, span: tt::TokenId) -> tt::TokenTree {
tt::TokenTree::from(tt::Leaf::from(tt::Punct {
char: c,
spacing: tt::Spacing::Alone,
- span: tt::TokenId::unspecified(),
+ span,
}))
}
- fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree {
- let lit = tt::Literal { text: doc_comment_text(comment), span: tt::TokenId::unspecified() };
+ fn mk_doc_literal(comment: &ast::Comment, span: tt::TokenId) -> tt::TokenTree {
+ let lit = tt::Literal { text: doc_comment_text(comment), span };
tt::TokenTree::from(tt::Leaf::from(lit))
}
@@ -480,6 +470,10 @@ impl TokenIdAlloc {
}
}
}
+
+ fn peek_next_id(&self) -> tt::TokenId {
+ tt::TokenId(self.next_id)
+ }
}
/// A raw token (straight from lexer) converter
@@ -502,7 +496,11 @@ trait SrcToken<Ctx>: std::fmt::Debug {
trait TokenConverter: Sized {
type Token: SrcToken<Self>;
- fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>>;
+ fn convert_doc_comment(
+ &self,
+ token: &Self::Token,
+ span: tt::TokenId,
+ ) -> Option<Vec<tt::TokenTree>>;
fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
@@ -532,9 +530,9 @@ impl<'a> SrcToken<RawConverter<'a>> for usize {
impl<'a> TokenConverter for RawConverter<'a> {
type Token = usize;
- fn convert_doc_comment(&self, &token: &usize) -> Option<Vec<tt::TokenTree>> {
+ fn convert_doc_comment(&self, &token: &usize, span: tt::TokenId) -> Option<Vec<tt::TokenTree>> {
let text = self.lexed.text(token);
- convert_doc_comment(&doc_comment(text))
+ convert_doc_comment(&doc_comment(text), span)
}
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -681,8 +679,12 @@ impl SrcToken<Converter> for SynToken {
impl TokenConverter for Converter {
type Token = SynToken;
- fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
- convert_doc_comment(token.token()?)
+ fn convert_doc_comment(
+ &self,
+ token: &Self::Token,
+ span: tt::TokenId,
+ ) -> Option<Vec<tt::TokenTree>> {
+ convert_doc_comment(token.token()?, span)
}
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
index f744481f3..59dbf1568 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
@@ -73,13 +73,6 @@ impl<'a> TtIter<'a> {
}
}
- pub(crate) fn expect_u32_literal(&mut self) -> Result<u32, ()> {
- match self.expect_literal()? {
- tt::Leaf::Literal(lit) => lit.text.parse().map_err(drop),
- _ => Err(()),
- }
- }
-
pub(crate) fn expect_single_punct(&mut self) -> Result<&'a tt::Punct, ()> {
match self.expect_leaf()? {
tt::Leaf::Punct(it) => Ok(it),
diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml
index 6e962abd7..09e62c352 100644
--- a/src/tools/rust-analyzer/crates/parser/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
[dependencies]
drop_bomb = "0.1.5"
-rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer.workspace = true
limit.workspace = true
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 15435a26c..1814e0e54 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -66,6 +66,10 @@ pub(crate) mod entry {
patterns::pattern_single(p);
}
+ pub(crate) fn pat_top(p: &mut Parser<'_>) {
+ patterns::pattern_top(p);
+ }
+
pub(crate) fn ty(p: &mut Parser<'_>) {
types::type_(p);
}
@@ -218,17 +222,22 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
// pub(self) struct S;
// pub(super) struct S;
+ // test_err crate_visibility_empty_recover
+ // pub() struct S;
+
// test pub_parens_typepath
// struct B(pub (super::A));
// struct B(pub (crate::A,));
- T![crate] | T![self] | T![super] | T![ident] if p.nth(2) != T![:] => {
+ T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
// If we are in a tuple struct, then the parens following `pub`
// might be an tuple field, not part of the visibility. So in that
// case we don't want to consume an identifier.
// test pub_tuple_field
// struct MyStruct(pub (u32, u32));
- if !(in_tuple_field && matches!(p.nth(1), T![ident])) {
+ // struct MyStruct(pub (u32));
+ // struct MyStruct(pub ());
+ if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
p.bump(T!['(']);
paths::use_path(p);
p.expect(T![')']);
@@ -243,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
paths::use_path(p);
p.expect(T![')']);
}
- _ => (),
+ _ => {}
}
}
m.complete(p, VISIBILITY);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index a884d8b6e..1cbd16632 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -4,8 +4,8 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST;
use super::*;
-pub(crate) use self::atom::{block_expr, match_arm_list};
-pub(super) use self::atom::{literal, LITERAL_FIRST};
+pub(crate) use atom::{block_expr, match_arm_list};
+pub(super) use atom::{literal, LITERAL_FIRST};
#[derive(PartialEq, Eq)]
pub(super) enum Semicolon {
@@ -188,47 +188,56 @@ struct Restrictions {
prefer_stmt: bool,
}
+enum Associativity {
+ Left,
+ Right,
+}
+
/// Binding powers of operators for a Pratt parser.
///
/// See <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html>
+///
+/// Note that Rust doesn't define associativity for some infix operators (e.g. `==` and `..`) and
+/// requires parentheses to disambiguate. We just treat them as left associative.
#[rustfmt::skip]
-fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) {
- const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
+fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) {
+ use Associativity::*;
+ const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left);
match p.current() {
- T![|] if p.at(T![||]) => (3, T![||]),
- T![|] if p.at(T![|=]) => (1, T![|=]),
- T![|] => (6, T![|]),
- T![>] if p.at(T![>>=]) => (1, T![>>=]),
- T![>] if p.at(T![>>]) => (9, T![>>]),
- T![>] if p.at(T![>=]) => (5, T![>=]),
- T![>] => (5, T![>]),
+ T![|] if p.at(T![||]) => (3, T![||], Left),
+ T![|] if p.at(T![|=]) => (1, T![|=], Right),
+ T![|] => (6, T![|], Left),
+ T![>] if p.at(T![>>=]) => (1, T![>>=], Right),
+ T![>] if p.at(T![>>]) => (9, T![>>], Left),
+ T![>] if p.at(T![>=]) => (5, T![>=], Left),
+ T![>] => (5, T![>], Left),
T![=] if p.at(T![=>]) => NOT_AN_OP,
- T![=] if p.at(T![==]) => (5, T![==]),
- T![=] => (1, T![=]),
- T![<] if p.at(T![<=]) => (5, T![<=]),
- T![<] if p.at(T![<<=]) => (1, T![<<=]),
- T![<] if p.at(T![<<]) => (9, T![<<]),
- T![<] => (5, T![<]),
- T![+] if p.at(T![+=]) => (1, T![+=]),
- T![+] => (10, T![+]),
- T![^] if p.at(T![^=]) => (1, T![^=]),
- T![^] => (7, T![^]),
- T![%] if p.at(T![%=]) => (1, T![%=]),
- T![%] => (11, T![%]),
- T![&] if p.at(T![&=]) => (1, T![&=]),
+ T![=] if p.at(T![==]) => (5, T![==], Left),
+ T![=] => (1, T![=], Right),
+ T![<] if p.at(T![<=]) => (5, T![<=], Left),
+ T![<] if p.at(T![<<=]) => (1, T![<<=], Right),
+ T![<] if p.at(T![<<]) => (9, T![<<], Left),
+ T![<] => (5, T![<], Left),
+ T![+] if p.at(T![+=]) => (1, T![+=], Right),
+ T![+] => (10, T![+], Left),
+ T![^] if p.at(T![^=]) => (1, T![^=], Right),
+ T![^] => (7, T![^], Left),
+ T![%] if p.at(T![%=]) => (1, T![%=], Right),
+ T![%] => (11, T![%], Left),
+ T![&] if p.at(T![&=]) => (1, T![&=], Right),
// If you update this, remember to update `expr_let()` too.
- T![&] if p.at(T![&&]) => (4, T![&&]),
- T![&] => (8, T![&]),
- T![/] if p.at(T![/=]) => (1, T![/=]),
- T![/] => (11, T![/]),
- T![*] if p.at(T![*=]) => (1, T![*=]),
- T![*] => (11, T![*]),
- T![.] if p.at(T![..=]) => (2, T![..=]),
- T![.] if p.at(T![..]) => (2, T![..]),
- T![!] if p.at(T![!=]) => (5, T![!=]),
- T![-] if p.at(T![-=]) => (1, T![-=]),
- T![-] => (10, T![-]),
- T![as] => (12, T![as]),
+ T![&] if p.at(T![&&]) => (4, T![&&], Left),
+ T![&] => (8, T![&], Left),
+ T![/] if p.at(T![/=]) => (1, T![/=], Right),
+ T![/] => (11, T![/], Left),
+ T![*] if p.at(T![*=]) => (1, T![*=], Right),
+ T![*] => (11, T![*], Left),
+ T![.] if p.at(T![..=]) => (2, T![..=], Left),
+ T![.] if p.at(T![..]) => (2, T![..], Left),
+ T![!] if p.at(T![!=]) => (5, T![!=], Left),
+ T![-] if p.at(T![-=]) => (1, T![-=], Right),
+ T![-] => (10, T![-], Left),
+ T![as] => (12, T![as], Left),
_ => NOT_AN_OP
}
@@ -273,7 +282,7 @@ fn expr_bp(
loop {
let is_range = p.at(T![..]) || p.at(T![..=]);
- let (op_bp, op) = current_op(p);
+ let (op_bp, op, associativity) = current_op(p);
if op_bp < bp {
break;
}
@@ -306,7 +315,11 @@ fn expr_bp(
}
}
- expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
+ let op_bp = match associativity {
+ Associativity::Left => op_bp + 1,
+ Associativity::Right => op_bp,
+ };
+ expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp);
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
}
Some((lhs, BlockLike::NotBlock))
@@ -417,7 +430,7 @@ fn postfix_expr(
allow_calls = true;
block_like = BlockLike::NotBlock;
}
- return (lhs, block_like);
+ (lhs, block_like)
}
fn postfix_dot_expr<const FLOAT_RECOVERY: bool>(
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index d051dd268..d8553d3f9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -12,6 +12,8 @@ use super::*;
// let _ = r"d";
// let _ = b"e";
// let _ = br"f";
+// let _ = c"g";
+// let _ = cr"h";
// }
pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
T![true],
@@ -22,6 +24,7 @@ pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
CHAR,
STRING,
BYTE_STRING,
+ C_STRING,
]);
pub(crate) fn literal(p: &mut Parser<'_>) -> Option<CompletedMarker> {
@@ -181,6 +184,16 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
let mut saw_comma = false;
let mut saw_expr = false;
+
+ // test_err tuple_expr_leading_comma
+ // fn foo() {
+ // (,);
+ // }
+ if p.eat(T![,]) {
+ p.error("expected expression");
+ saw_comma = true;
+ }
+
while !p.at(EOF) && !p.at(T![')']) {
saw_expr = true;
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
index 919d9b91e..e589b6993 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
@@ -28,6 +28,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
BYTE,
STRING,
BYTE_STRING,
+ C_STRING,
])
.union(types::TYPE_FIRST);
@@ -35,7 +36,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
// type T = S<i32>;
fn generic_arg(p: &mut Parser<'_>) -> bool {
match p.current() {
- LIFETIME_IDENT => lifetime_arg(p),
+ LIFETIME_IDENT if !p.nth_at(1, T![+]) => lifetime_arg(p),
T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
k if k.is_literal() => const_arg(p),
// test associated_type_bounds
@@ -76,6 +77,29 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
}
}
}
+ IDENT if p.nth_at(1, T!['(']) => {
+ let m = p.start();
+ name_ref(p);
+ params::param_list_fn_trait(p);
+ if p.at(T![:]) && !p.at(T![::]) {
+ // test associated_return_type_bounds
+ // fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
+ generic_params::bounds(p);
+ m.complete(p, ASSOC_TYPE_ARG);
+ } else {
+ // test bare_dyn_types_with_paren_as_generic_args
+ // type A = S<Fn(i32)>;
+ // type A = S<Fn(i32) + Send>;
+ // type B = S<Fn(i32) -> i32>;
+ // type C = S<Fn(i32) -> i32 + Send>;
+ opt_ret_type(p);
+ let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
+ let m = paths::type_path_for_qualifier(p, m);
+ let m = m.precede(p).complete(p, PATH_TYPE);
+ let m = types::opt_type_bounds_as_dyn_trait_type(p, m);
+ m.precede(p).complete(p, TYPE_ARG);
+ }
+ }
_ if p.at_ts(types::TYPE_FIRST) => type_arg(p),
_ => return false,
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index 5e0951bf8..1c056819f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -19,7 +19,7 @@ use super::*;
// struct S;
pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) {
attributes::inner_attrs(p);
- while !p.at(EOF) && !(p.at(T!['}']) && stop_on_r_curly) {
+ while !(p.at(EOF) || (p.at(T!['}']) && stop_on_r_curly)) {
item_or_macro(p, stop_on_r_curly);
}
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
index 26490aa97..01b8f9e91 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -136,6 +136,7 @@ fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
Mode::Type => {
// test typepathfn_with_coloncolon
// type F = Start::(Middle) -> (Middle)::End;
+ // type GenericArg = S<Start(Middle)::End>;
if p.at(T![::]) && p.nth_at(2, T!['(']) {
p.bump(T![::]);
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
index 5f4977886..39ded41bb 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
@@ -5,6 +5,7 @@ pub(super) const PATTERN_FIRST: TokenSet =
T![box],
T![ref],
T![mut],
+ T![const],
T!['('],
T!['['],
T![&],
@@ -15,6 +16,10 @@ pub(super) const PATTERN_FIRST: TokenSet =
const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]]));
+/// Set of possible tokens at the start of a range pattern's end bound.
+const RANGE_PAT_END_FIRST: TokenSet =
+ expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
+
pub(crate) fn pattern(p: &mut Parser<'_>) {
pattern_r(p, PAT_RECOVERY_SET);
}
@@ -105,6 +110,52 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
return;
}
+ // test exclusive_range_pat
+ // fn main() {
+ // match 42 {
+ // ..0 => {}
+ // 1..2 => {}
+ // }
+ // }
+
+ // test dot_dot_pat
+ // fn main() {
+ // let .. = ();
+ // //
+ // // Tuples
+ // //
+ // let (a, ..) = ();
+ // let (a, ..,) = ();
+ // let Tuple(a, ..) = ();
+ // let Tuple(a, ..,) = ();
+ // let (.., ..) = ();
+ // let Tuple(.., ..) = ();
+ // let (.., a, ..) = ();
+ // let Tuple(.., a, ..) = ();
+ // //
+ // // Slices
+ // //
+ // let [..] = ();
+ // let [head, ..] = ();
+ // let [head, tail @ ..] = ();
+ // let [head, .., cons] = ();
+ // let [head, mid @ .., cons] = ();
+ // let [head, .., .., cons] = ();
+ // let [head, .., mid, tail @ ..] = ();
+ // let [head, .., mid, .., cons] = ();
+ // }
+ if p.at(T![..]) {
+ let m = p.start();
+ p.bump(T![..]);
+ if p.at_ts(RANGE_PAT_END_FIRST) {
+ atom_pat(p, recovery_set);
+ m.complete(p, RANGE_PAT);
+ } else {
+ m.complete(p, REST_PAT);
+ }
+ return;
+ }
+
if let Some(lhs) = atom_pat(p, recovery_set) {
for range_op in [T![...], T![..=], T![..]] {
if p.at(range_op) {
@@ -173,7 +224,6 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarke
_ if paths::is_path_start(p) => path_or_macro_pat(p),
_ if is_literal_pat_start(p) => literal_pat(p),
- T![.] if p.at(T![..]) => rest_pat(p),
T![_] => wildcard_pat(p),
T![&] => ref_pat(p),
T!['('] => tuple_pat(p),
@@ -334,39 +384,6 @@ fn wildcard_pat(p: &mut Parser<'_>) -> CompletedMarker {
m.complete(p, WILDCARD_PAT)
}
-// test dot_dot_pat
-// fn main() {
-// let .. = ();
-// //
-// // Tuples
-// //
-// let (a, ..) = ();
-// let (a, ..,) = ();
-// let Tuple(a, ..) = ();
-// let Tuple(a, ..,) = ();
-// let (.., ..) = ();
-// let Tuple(.., ..) = ();
-// let (.., a, ..) = ();
-// let Tuple(.., a, ..) = ();
-// //
-// // Slices
-// //
-// let [..] = ();
-// let [head, ..] = ();
-// let [head, tail @ ..] = ();
-// let [head, .., cons] = ();
-// let [head, mid @ .., cons] = ();
-// let [head, .., .., cons] = ();
-// let [head, .., mid, tail @ ..] = ();
-// let [head, .., mid, .., cons] = ();
-// }
-fn rest_pat(p: &mut Parser<'_>) -> CompletedMarker {
- assert!(p.at(T![..]));
- let m = p.start();
- p.bump(T![..]);
- m.complete(p, REST_PAT)
-}
-
// test ref_pat
// fn main() {
// let &a = ();
@@ -396,6 +413,16 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
let mut has_comma = false;
let mut has_pat = false;
let mut has_rest = false;
+
+ // test_err tuple_pat_leading_comma
+ // fn foo() {
+ // let (,);
+ // }
+ if p.eat(T![,]) {
+ p.error("expected pattern");
+ has_comma = true;
+ }
+
while !p.at(EOF) && !p.at(T![')']) {
has_pat = true;
if !p.at_ts(PAT_TOP_FIRST) {
@@ -483,6 +510,14 @@ fn box_pat(p: &mut Parser<'_>) -> CompletedMarker {
// fn main() {
// let const { 15 } = ();
// let const { foo(); bar() } = ();
+//
+// match 42 {
+// const { 0 } .. const { 1 } => (),
+// .. const { 0 } => (),
+// const { 2 } .. => (),
+// }
+//
+// let (const { () },) = ();
// }
fn const_block_pat(p: &mut Parser<'_>) -> CompletedMarker {
assert!(p.at(T![const]));
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
index 7d0b156c5..96a6cdeaa 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
@@ -15,6 +15,7 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
T![impl],
T![dyn],
T![Self],
+ LIFETIME_IDENT,
]));
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
@@ -49,6 +50,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
// Some path types are not allowed to have bounds (no plus)
T![<] => path_type_(p, allow_bounds),
_ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
+ LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
_ => {
p.err_recover("expected type", TYPE_RECOVERY_SET);
}
@@ -59,7 +61,7 @@ pub(super) fn ascription(p: &mut Parser<'_>) {
assert!(p.at(T![:]));
p.bump(T![:]);
if p.at(T![=]) {
- // recover from `let x: = expr;`, `const X: = expr;` and similars
+ // recover from `let x: = expr;`, `const X: = expr;` and similar
// hopefully no type starts with `=`
p.error("missing type");
return;
@@ -151,7 +153,9 @@ fn array_or_slice_type(p: &mut Parser<'_>) {
// type T = [(); 92];
T![;] => {
p.bump(T![;]);
+ let m = p.start();
expressions::expr(p);
+ m.complete(p, CONST_ARG);
p.expect(T![']']);
ARRAY_TYPE
}
@@ -275,6 +279,15 @@ fn dyn_trait_type(p: &mut Parser<'_>) {
m.complete(p, DYN_TRAIT_TYPE);
}
+// test bare_dyn_types_with_leading_lifetime
+// type A = 'static + Trait;
+// type B = S<'static + Trait>;
+fn bare_dyn_trait_type(p: &mut Parser<'_>) {
+ let m = p.start();
+ generic_params::bounds_without_colon(p);
+ m.complete(p, DYN_TRAIT_TYPE);
+}
+
// test path_type
// type A = Foo;
// type B = ::Foo;
@@ -326,13 +339,16 @@ pub(super) fn path_type_(p: &mut Parser<'_>, allow_bounds: bool) {
/// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE
/// with a TYPE_BOUND_LIST
-fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedMarker) {
+pub(super) fn opt_type_bounds_as_dyn_trait_type(
+ p: &mut Parser<'_>,
+ type_marker: CompletedMarker,
+) -> CompletedMarker {
assert!(matches!(
type_marker.kind(),
SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE
));
if !p.at(T![+]) {
- return;
+ return type_marker;
}
// First create a TYPE_BOUND from the completed PATH_TYPE
@@ -349,5 +365,5 @@ fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedM
let m = generic_params::bounds_without_colon_m(p, m);
// Finally precede everything with DYN_TRAIT_TYPE
- m.precede(p).complete(p, DYN_TRAIT_TYPE);
+ m.precede(p).complete(p, DYN_TRAIT_TYPE)
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 100deff46..e4dce21f3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -36,7 +36,7 @@ impl<'a> LexedStr<'a> {
};
for token in rustc_lexer::tokenize(&text[conv.offset..]) {
- let token_text = &text[conv.offset..][..token.len];
+ let token_text = &text[conv.offset..][..token.len as usize];
conv.extend_token(&token.kind, token_text);
}
@@ -49,8 +49,8 @@ impl<'a> LexedStr<'a> {
return None;
}
- let token = rustc_lexer::first_token(text);
- if token.len != text.len() {
+ let token = rustc_lexer::tokenize(text).next()?;
+ if token.len as usize != text.len() {
return None;
}
@@ -175,6 +175,10 @@ impl<'a> Converter<'a> {
rustc_lexer::TokenKind::Ident => {
SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
}
+ rustc_lexer::TokenKind::InvalidIdent => {
+ err = "Ident contains invalid characters";
+ IDENT
+ }
rustc_lexer::TokenKind::RawIdent => IDENT,
rustc_lexer::TokenKind::Literal { kind, .. } => {
@@ -221,6 +225,7 @@ impl<'a> Converter<'a> {
err = "unknown literal prefix";
IDENT
}
+ rustc_lexer::TokenKind::Eof => EOF,
}
};
@@ -268,35 +273,30 @@ impl<'a> Converter<'a> {
}
BYTE_STRING
}
- rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => {
- if let Some(raw_str_err) = raw_str_err {
- err = match raw_str_err {
- rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal",
- rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
- "Missing trailing `\"` to terminate the raw string literal"
- } else {
- "Missing trailing `\"` with `#` symbols to terminate the raw string literal"
- },
- rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols",
- };
- };
+ rustc_lexer::LiteralKind::CStr { terminated } => {
+ if !terminated {
+ err = "Missing trailing `\"` symbol to terminate the string literal";
+ }
+ C_STRING
+ }
+ rustc_lexer::LiteralKind::RawStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
STRING
}
- rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => {
- if let Some(raw_str_err) = raw_str_err {
- err = match raw_str_err {
- rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal",
- rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
- "Missing trailing `\"` to terminate the raw byte string literal"
- } else {
- "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"
- },
- rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols",
- };
- };
-
+ rustc_lexer::LiteralKind::RawByteStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
BYTE_STRING
}
+ rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
+ C_STRING
+ }
};
let err = if err.is_empty() { None } else { Some(err) };
diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs
index 8c5aed023..1aba1f767 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs
@@ -131,6 +131,7 @@ pub enum PrefixEntryPoint {
Block,
Stmt,
Pat,
+ PatTop,
Ty,
Expr,
Path,
@@ -145,6 +146,7 @@ impl PrefixEntryPoint {
PrefixEntryPoint::Block => grammar::entry::prefix::block,
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
+ PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top,
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
PrefixEntryPoint::Path => grammar::entry::prefix::path,
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 280416ae7..ef413c637 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -205,7 +205,7 @@ impl<'t> Parser<'t> {
marker.bomb.defuse();
marker = new_marker;
};
- self.pos += 1 as usize;
+ self.pos += 1;
self.push_event(Event::FloatSplitHack { ends_in_dot });
(ends_in_dot, marker)
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 47e4adcbb..5cdb39700 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -46,10 +46,8 @@ impl<'a> LexedStr<'a> {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER {
- if !self.text(i).ends_with('.') {
- res.was_joint();
- }
+ if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') {
+ res.was_joint();
}
}
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 cd87b304a..a8fbcfacf 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
@@ -117,6 +117,7 @@ pub enum SyntaxKind {
BYTE,
STRING,
BYTE_STRING,
+ C_STRING,
ERROR,
IDENT,
WHITESPACE,
@@ -245,6 +246,7 @@ pub enum SyntaxKind {
GENERIC_PARAM,
LIFETIME_PARAM,
TYPE_PARAM,
+ RETURN_TYPE_ARG,
CONST_PARAM,
GENERIC_ARG_LIST,
LIFETIME,
@@ -378,7 +380,7 @@ impl SyntaxKind {
)
}
pub fn is_literal(self) -> bool {
- matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING)
+ matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING)
}
pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
let kw = match ident {
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index 40f92e588..11f9c34ab 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -33,8 +33,7 @@ fn stmt() {
fn pat() {
check(PrefixEntryPoint::Pat, "x y", "x");
check(PrefixEntryPoint::Pat, "fn f() {}", "fn");
- // FIXME: This one is wrong, we should consume only one pattern.
- check(PrefixEntryPoint::Pat, ".. ..", ".. ..");
+ check(PrefixEntryPoint::Pat, ".. ..", "..");
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
index 6ec1780c3..cab02d38a 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
index d65f1bb2f..0486a1e8e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\x7f" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
index 0f9e0a165..41e3455c1 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"🦀" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
index 202dcd2d4..a11208a81 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
index d45485b52..10a47ab84 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\n" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
index 1bfabbc3a..b41ea3a17 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
@@ -1 +1 @@
-BYTE_STRING "br##\" " error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\" " error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
index 104ab8aae..63b8a5af8 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\u{20AA}" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
index 71b20fd19..096bb9403 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
@@ -1 +1 @@
-STRING "r##\"" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
index dc106dd24..f0ad200fe 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
@@ -1 +1 @@
-STRING "r##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\x7f" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
index 30ee029f6..bc5996d1e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
@@ -1 +1 @@
-STRING "r##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"🦀" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
index 8a6f6cc43..b48ec5dda 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
@@ -1 +1 @@
-STRING "r##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
index f46eff251..9f32f6777 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
@@ -1 +1 @@
-STRING "r##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\n" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
index 49b6afea4..2804a43cf 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
@@ -1 +1 @@
-STRING "r##\" " error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\" " error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
index d10d6d8e8..eb0a2d2da 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
@@ -1 +1 @@
-STRING "r##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\u{20AA}" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
index cf942c92f..52a7f03b6 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
@@ -1 +1 @@
-BYTE_STRING "br##" error: Missing `"` symbol after `#` symbols to begin the raw byte string literal
+BYTE_STRING "br##" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
index 042769c27..da5550d4c 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
@@ -1,4 +1,4 @@
-BYTE_STRING "br## " error: Missing `"` symbol after `#` symbols to begin the raw byte string literal
+BYTE_STRING "br## " error: Invalid raw string literal
IDENT "I"
WHITESPACE " "
IDENT "lack"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
index 2f7c7529a..50b962e77 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
@@ -1 +1 @@
-STRING "r##" error: Missing `"` symbol after `#` symbols to begin the raw string literal
+STRING "r##" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
index 4a06b0abe..1f484299a 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
@@ -1,4 +1,4 @@
-STRING "r## " error: Missing `"` symbol after `#` symbols to begin the raw string literal
+STRING "r## " error: Invalid raw string literal
IDENT "I"
WHITESPACE " "
IDENT "lack"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
index 674c8d536..674c8d536 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs
index 2792c2084..2792c2084 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
index 4b2a74036..4b2a74036 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs
index db32b98df..db32b98df 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast
new file mode 100644
index 000000000..0fe4ca42d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast
@@ -0,0 +1,18 @@
+SOURCE_FILE
+ STRUCT
+ VISIBILITY
+ PUB_KW "pub"
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ ERROR
+ R_PAREN ")"
+ WHITESPACE " "
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "S"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 4: expected identifier
+error 5: expected R_PAREN
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs
new file mode 100644
index 000000000..e8cf9e669
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs
@@ -0,0 +1 @@
+pub() struct S;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast
new file mode 100644
index 000000000..3fbc0da40
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast
@@ -0,0 +1,24 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ TUPLE_EXPR
+ L_PAREN "("
+ COMMA ","
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 17: expected expression
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs
new file mode 100644
index 000000000..12fab59a7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ (,);
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast
new file mode 100644
index 000000000..9c8837292
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast
@@ -0,0 +1,26 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_PAT
+ L_PAREN "("
+ COMMA ","
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 21: expected pattern
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs
new file mode 100644
index 000000000..de168521e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ let (,);
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
index 2a5c644d4..0d50144b7 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
@@ -14,8 +14,9 @@ SOURCE_FILE
R_PAREN ")"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "92"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "92"
R_BRACK "]"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
index 403c265ea..fe73d9dfe 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
@@ -131,6 +131,30 @@ SOURCE_FILE
LITERAL
BYTE_STRING "br\"f\""
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ C_STRING "c\"g\""
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ C_STRING "cr\"h\""
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
index 2e11a5a6e..e7f235a83 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
@@ -9,4 +9,6 @@ fn foo() {
let _ = r"d";
let _ = b"e";
let _ = br"f";
+ let _ = c"g";
+ let _ = cr"h";
}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
index 59de2b9f1..593867a7b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
@@ -74,6 +74,126 @@ SOURCE_FILE
L_PAREN "("
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n\n "
+ EXPR_STMT
+ MATCH_EXPR
+ MATCH_KW "match"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ MATCH_ARM_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ DOT2 ".."
+ WHITESPACE " "
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ DOT2 ".."
+ WHITESPACE " "
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ DOT2 ".."
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE "\n\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_PAT
+ L_PAREN "("
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ COMMA ","
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
index dce9defac..6ecdee849 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
@@ -1,4 +1,12 @@
fn main() {
let const { 15 } = ();
let const { foo(); bar() } = ();
+
+ match 42 {
+ const { 0 } .. const { 1 } => (),
+ .. const { 0 } => (),
+ const { 2 } .. => (),
+ }
+
+ let (const { () },) = ();
}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
index a23ddf69f..c78d16f06 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
@@ -28,3 +28,42 @@ SOURCE_FILE
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n"
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "MyStruct"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ PAREN_TYPE
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "u32"
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "MyStruct"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
index 00d8feba9..6f725fb7b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
@@ -1 +1,3 @@
struct MyStruct(pub (u32, u32));
+struct MyStruct(pub (u32));
+struct MyStruct(pub ());
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
index b47a5a5c1..67277d063 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
@@ -41,3 +41,41 @@ SOURCE_FILE
IDENT "End"
SEMICOLON ";"
WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "GenericArg"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Start"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Middle"
+ R_PAREN ")"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "End"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
index 8efd93a7f..8c54f6704 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
@@ -1 +1,2 @@
type F = Start::(Middle) -> (Middle)::End;
+type GenericArg = S<Start(Middle)::End>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast
new file mode 100644
index 000000000..fd2c422d0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "main"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_EXPR
+ MATCH_KW "match"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ MATCH_ARM_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ DOT2 ".."
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "1"
+ DOT2 ".."
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs
new file mode 100644
index 000000000..e80505d8b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs
@@ -0,0 +1,6 @@
+fn main() {
+ match 42 {
+ ..0 => {}
+ 1..2 => {}
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast
new file mode 100644
index 000000000..2fa52068c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast
@@ -0,0 +1,102 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ GENERIC_PARAM_LIST
+ L_ANGLE "<"
+ TYPE_PARAM
+ NAME
+ IDENT "T"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "bar"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "baz"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ COMMA ","
+ WHITESPACE " "
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ R_ANGLE ">"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs
new file mode 100644
index 000000000..42029ac59
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs
@@ -0,0 +1 @@
+fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast
new file mode 100644
index 000000000..d7e67fbcd
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'static"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "B"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'static"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs
new file mode 100644
index 000000000..3e9a9a29d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs
@@ -0,0 +1,2 @@
+type A = 'static + Trait;
+type B = S<'static + Trait>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast
new file mode 100644
index 000000000..d5f97bad8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast
@@ -0,0 +1,175 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "B"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "C"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs
new file mode 100644
index 000000000..800002b1b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs
@@ -0,0 +1,4 @@
+type A = S<Fn(i32)>;
+type A = S<Fn(i32) + Send>;
+type B = S<Fn(i32) -> i32>;
+type C = S<Fn(i32) -> i32 + Send>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
index ae08c0756..438025728 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
@@ -183,4 +183,273 @@ SOURCE_FILE
COMMENT "//---&*1 - --2 * 9;"
WHITESPACE "\n"
R_CURLY "}"
+ WHITESPACE "\n\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "right_associative"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ PLUSEQ "+="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ MINUSEQ "-="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ STAREQ "*="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ SLASHEQ "/="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ PERCENTEQ "%="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ AMPEQ "&="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ PIPEEQ "|="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ CARETEQ "^="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ SHLEQ "<<="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ SHREQ ">>="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "mixed_associativity"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ COMMENT "// (a + b) = (c += ((d * e) = f))"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ PLUSEQ "+="
+ WHITESPACE " "
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ STAR "*"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "f"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
index cc9598470..7ee3013a0 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
@@ -12,3 +12,16 @@ fn binding_power() {
//1 = 2 .. 3;
//---&*1 - --2 * 9;
}
+
+fn right_associative() {
+ a = b = c;
+ a = b += c -= d;
+ a = b *= c /= d %= e;
+ a = b &= c |= d ^= e;
+ a = b <<= c >>= d;
+}
+
+fn mixed_associativity() {
+ // (a + b) = (c += ((d * e) = f))
+ a + b = c += d * e = f;
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
index 44423581e..3965ae959 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
@@ -51,8 +51,9 @@ SOURCE_FILE
IDENT "i32"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "1"
R_BRACK "]"
R_PAREN ")"
SEMICOLON ";"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
index 3b02c3f96..f3c85b45b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
@@ -24,8 +24,9 @@ SOURCE_FILE
IDENT "u8"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "1"
R_BRACK "]"
WHITESPACE " "
R_CURLY "}"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
index bef138071..fad574a47 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
@@ -60,7 +60,7 @@ SOURCE_FILE
IDENT "doc"
TOKEN_TREE
L_PAREN "("
- STRING "\"Being validated is not affected by duplcates\""
+ STRING "\"Being validated is not affected by duplicates\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
index f16c4566e..0969ea165 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
@@ -3,7 +3,7 @@ fn inner() {
//! As are ModuleDoc style comments
{
#![doc("Inner attributes are allowed in blocks used as statements")]
- #![doc("Being validated is not affected by duplcates")]
+ #![doc("Being validated is not affected by duplicates")]
//! As are ModuleDoc style comments
};
{
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
index e8b836dfb..ce75c5518 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
@@ -168,42 +168,46 @@ SOURCE_FILE
WHITESPACE "\n "
EXPR_STMT
BIN_EXPR
- BIN_EXPR
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- ARG_LIST
- L_PAREN "("
- RANGE_EXPR
- DOT2 ".."
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- METHOD_CALL_EXPR
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- ARG_LIST
- L_PAREN "("
- LITERAL
- INT_NUMBER "0"
- R_PAREN ")"
- DOT "."
- WHITESPACE "\n "
- NAME_REF
- IDENT "Ok"
- ARG_LIST
- L_PAREN "("
- UNDERSCORE_EXPR
- UNDERSCORE "_"
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ ARG_LIST
+ L_PAREN "("
+ RANGE_EXPR
+ DOT2 ".."
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ ARG_LIST
+ L_PAREN "("
+ LITERAL
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Ok"
+ ARG_LIST
+ L_PAREN "("
+ UNDERSCORE_EXPR
+ UNDERSCORE "_"
+ R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
index 9d3e86603..d223b11f2 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
@@ -4,7 +4,7 @@ fn foo() {
(_) = ..;
struct S { a: i32 }
S { .. } = S { ..S::default() };
- Some(..) = Some(0).
+ Some(..) = Some(0);
Ok(_) = 0;
let (a, b);
[a, .., b] = [1, .., 2];
diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml
index e24e6ecef..28b54be52 100644
--- a/src/tools/rust-analyzer/crates/paths/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml
@@ -15,4 +15,4 @@ doctest = false
# Adding this dep sadly puts a lot of rust-analyzer crates after the
# serde-derive crate. Even though we don't activate the derive feature here,
# someone else in the crate graph certainly does!
-# serde = "1"
+# serde.workspace = true
diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs
index 6ae23ac84..e0c20a414 100644
--- a/src/tools/rust-analyzer/crates/paths/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs
@@ -140,6 +140,11 @@ impl AbsPath {
self.0.parent().map(AbsPath::assert)
}
+ /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards.
+ pub fn absolutize(&self, path: impl AsRef<Path>) -> AbsPathBuf {
+ self.join(path).normalize()
+ }
+
/// Equivalent of [`Path::join`] for `AbsPath`.
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
self.as_ref().join(path).try_into().unwrap()
@@ -166,6 +171,10 @@ impl AbsPath {
AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
}
+ pub fn canonicalize(&self) -> ! {
+ panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430")
+ }
+
/// Equivalent of [`Path::strip_prefix`] for `AbsPath`.
///
/// Returns a relative path.
@@ -179,6 +188,13 @@ impl AbsPath {
self.0.ends_with(&suffix.0)
}
+ pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
+ Some((
+ self.file_stem()?.to_str()?,
+ self.extension().and_then(|extension| extension.to_str()),
+ ))
+ }
+
// region:delegate-methods
// Note that we deliberately don't implement `Deref<Target = Path>` here.
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 28469b832..d3486e755 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -19,9 +19,10 @@ object = { version = "0.30.2", default-features = false, features = [
"macho",
"pe",
] }
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
+serde.workspace = true
+serde_json = { workspace = true, features = ["unbounded_depth"] }
tracing = "0.1.37"
+triomphe.workspace = true
memmap2 = "0.5.4"
snap = "1.1.0"
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 90d06967e..1603458f7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -12,11 +12,8 @@ mod process;
mod version;
use paths::AbsPathBuf;
-use std::{
- ffi::OsStr,
- fmt, io,
- sync::{Arc, Mutex},
-};
+use std::{fmt, io, sync::Mutex};
+use triomphe::Arc;
use serde::{Deserialize, Serialize};
@@ -54,18 +51,8 @@ pub struct MacroDylib {
}
impl MacroDylib {
- // FIXME: this is buggy due to TOCTOU, we should check the version in the
- // macro process instead.
- pub fn new(path: AbsPathBuf) -> io::Result<MacroDylib> {
- let _p = profile::span("MacroDylib::new");
-
- let info = version::read_dylib_info(&path)?;
- if info.version.0 < 1 || info.version.1 < 47 {
- let msg = format!("proc-macro {} built by {info:#?} is not supported by rust-analyzer, please update your Rust version.", path.display());
- return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
- }
-
- Ok(MacroDylib { path })
+ pub fn new(path: AbsPathBuf) -> MacroDylib {
+ MacroDylib { path }
}
}
@@ -113,11 +100,8 @@ pub struct MacroPanic {
impl ProcMacroServer {
/// Spawns an external process as the proc macro server and returns a client connected to it.
- pub fn spawn(
- process_path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
- ) -> io::Result<ProcMacroServer> {
- let process = ProcMacroProcessSrv::run(process_path, args)?;
+ pub fn spawn(process_path: AbsPathBuf) -> io::Result<ProcMacroServer> {
+ let process = ProcMacroProcessSrv::run(process_path)?;
Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) })
}
@@ -156,15 +140,16 @@ impl ProcMacro {
attr: Option<&tt::Subtree>,
env: Vec<(String, String)>,
) -> Result<Result<tt::Subtree, PanicMessage>, ServerError> {
+ let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version();
let current_dir = env
.iter()
.find(|(name, _)| name == "CARGO_MANIFEST_DIR")
.map(|(_, value)| value.clone());
let task = ExpandMacro {
- macro_body: FlatTree::new(subtree),
+ macro_body: FlatTree::new(subtree, version),
macro_name: self.name.to_string(),
- attributes: attr.map(FlatTree::new),
+ attributes: attr.map(|subtree| FlatTree::new(subtree, version)),
lib: self.dylib_path.to_path_buf().into(),
env,
current_dir,
@@ -173,7 +158,9 @@ impl ProcMacro {
let request = msg::Request::ExpandMacro(task);
let response = self.process.lock().unwrap_or_else(|e| e.into_inner()).send_task(request)?;
match response {
- msg::Response::ExpandMacro(it) => Ok(it.map(FlatTree::to_subtree)),
+ msg::Response::ExpandMacro(it) => {
+ Ok(it.map(|tree| FlatTree::to_subtree(tree, version)))
+ }
msg::Response::ListMacros(..) | msg::Response::ApiVersionCheck(..) => {
Err(ServerError { message: "unexpected response".to_string(), io: None })
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
index 4040efe93..4b01643c2 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
@@ -12,8 +12,12 @@ use crate::ProcMacroKind;
pub use crate::msg::flat::FlatTree;
+// The versions of the server protocol
pub const NO_VERSION_CHECK_VERSION: u32 = 0;
-pub const CURRENT_API_VERSION: u32 = 1;
+pub const VERSION_CHECK_VERSION: u32 = 1;
+pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
+
+pub const CURRENT_API_VERSION: u32 = ENCODE_CLOSE_SPAN_VERSION;
#[derive(Debug, Serialize, Deserialize)]
pub enum Request {
@@ -146,7 +150,7 @@ mod tests {
fn test_proc_macro_rpc_works() {
let tt = fixture_token_tree();
let task = ExpandMacro {
- macro_body: FlatTree::new(&tt),
+ macro_body: FlatTree::new(&tt, CURRENT_API_VERSION),
macro_name: Default::default(),
attributes: None,
lib: std::env::current_dir().unwrap(),
@@ -158,6 +162,6 @@ mod tests {
// println!("{}", json);
let back: ExpandMacro = serde_json::from_str(&json).unwrap();
- assert_eq!(tt, back.macro_body.to_subtree());
+ assert_eq!(tt, back.macro_body.to_subtree(CURRENT_API_VERSION));
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
index fd3202e0b..44245336f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
@@ -39,7 +39,10 @@ use std::collections::{HashMap, VecDeque};
use serde::{Deserialize, Serialize};
-use crate::tt::{self, TokenId};
+use crate::{
+ msg::ENCODE_CLOSE_SPAN_VERSION,
+ tt::{self, TokenId},
+};
#[derive(Serialize, Deserialize, Debug)]
pub struct FlatTree {
@@ -52,7 +55,8 @@ pub struct FlatTree {
}
struct SubtreeRepr {
- id: tt::TokenId,
+ open: tt::TokenId,
+ close: tt::TokenId,
kind: tt::DelimiterKind,
tt: [u32; 2],
}
@@ -74,7 +78,7 @@ struct IdentRepr {
}
impl FlatTree {
- pub fn new(subtree: &tt::Subtree) -> FlatTree {
+ pub fn new(subtree: &tt::Subtree, version: u32) -> FlatTree {
let mut w = Writer {
string_table: HashMap::new(),
work: VecDeque::new(),
@@ -89,7 +93,11 @@ impl FlatTree {
w.write(subtree);
return FlatTree {
- subtree: write_vec(w.subtree, SubtreeRepr::write),
+ subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+ write_vec(w.subtree, SubtreeRepr::write_with_close_span)
+ } else {
+ write_vec(w.subtree, SubtreeRepr::write)
+ },
literal: write_vec(w.literal, LiteralRepr::write),
punct: write_vec(w.punct, PunctRepr::write),
ident: write_vec(w.ident, IdentRepr::write),
@@ -102,9 +110,13 @@ impl FlatTree {
}
}
- pub fn to_subtree(self) -> tt::Subtree {
+ pub fn to_subtree(self, version: u32) -> tt::Subtree {
return Reader {
- subtree: read_vec(self.subtree, SubtreeRepr::read),
+ subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+ read_vec(self.subtree, SubtreeRepr::read_with_close_span)
+ } else {
+ read_vec(self.subtree, SubtreeRepr::read)
+ },
literal: read_vec(self.literal, LiteralRepr::read),
punct: read_vec(self.punct, PunctRepr::read),
ident: read_vec(self.ident, IdentRepr::read),
@@ -130,9 +142,9 @@ impl SubtreeRepr {
tt::DelimiterKind::Brace => 2,
tt::DelimiterKind::Bracket => 3,
};
- [self.id.0, kind, self.tt[0], self.tt[1]]
+ [self.open.0, kind, self.tt[0], self.tt[1]]
}
- fn read([id, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
+ fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
let kind = match kind {
0 => tt::DelimiterKind::Invisible,
1 => tt::DelimiterKind::Parenthesis,
@@ -140,7 +152,26 @@ impl SubtreeRepr {
3 => tt::DelimiterKind::Bracket,
other => panic!("bad kind {other}"),
};
- SubtreeRepr { id: TokenId(id), kind, tt: [lo, len] }
+ SubtreeRepr { open: TokenId(open), close: TokenId::UNSPECIFIED, kind, tt: [lo, len] }
+ }
+ fn write_with_close_span(self) -> [u32; 5] {
+ let kind = match self.kind {
+ tt::DelimiterKind::Invisible => 0,
+ tt::DelimiterKind::Parenthesis => 1,
+ tt::DelimiterKind::Brace => 2,
+ tt::DelimiterKind::Bracket => 3,
+ };
+ [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
+ }
+ fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
+ let kind = match kind {
+ 0 => tt::DelimiterKind::Invisible,
+ 1 => tt::DelimiterKind::Parenthesis,
+ 2 => tt::DelimiterKind::Brace,
+ 3 => tt::DelimiterKind::Bracket,
+ other => panic!("bad kind {other}"),
+ };
+ SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] }
}
}
@@ -244,9 +275,10 @@ impl<'a> Writer<'a> {
fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 {
let idx = self.subtree.len();
- let delimiter_id = subtree.delimiter.open;
+ let open = subtree.delimiter.open;
+ let close = subtree.delimiter.close;
let delimiter_kind = subtree.delimiter.kind;
- self.subtree.push(SubtreeRepr { id: delimiter_id, kind: delimiter_kind, tt: [!0, !0] });
+ self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] });
self.work.push_back((idx, subtree));
idx as u32
}
@@ -277,11 +309,7 @@ impl Reader {
let repr = &self.subtree[i];
let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
let s = tt::Subtree {
- delimiter: tt::Delimiter {
- open: repr.id,
- close: TokenId::UNSPECIFIED,
- kind: repr.kind,
- },
+ delimiter: tt::Delimiter { open: repr.open, close: repr.close, kind: repr.kind },
token_trees: token_trees
.iter()
.copied()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 1ccbd780f..9a20fa63e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -1,7 +1,6 @@
//! Handle process life-time and message passing for proc-macro client
use std::{
- ffi::{OsStr, OsString},
io::{self, BufRead, BufReader, Write},
process::{Child, ChildStdin, ChildStdout, Command, Stdio},
};
@@ -23,12 +22,9 @@ pub(crate) struct ProcMacroProcessSrv {
}
impl ProcMacroProcessSrv {
- pub(crate) fn run(
- process_path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
- ) -> io::Result<ProcMacroProcessSrv> {
+ pub(crate) fn run(process_path: AbsPathBuf) -> io::Result<ProcMacroProcessSrv> {
let create_srv = |null_stderr| {
- let mut process = Process::run(process_path.clone(), args.clone(), null_stderr)?;
+ let mut process = Process::run(process_path.clone(), null_stderr)?;
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 })
@@ -56,6 +52,10 @@ impl ProcMacroProcessSrv {
}
}
+ pub(crate) fn version(&self) -> u32 {
+ self.version
+ }
+
pub(crate) fn version_check(&mut self) -> Result<u32, ServerError> {
let request = Request::ApiVersionCheck {};
let response = self.send_task(request)?;
@@ -96,13 +96,8 @@ struct Process {
}
impl Process {
- fn run(
- path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>>,
- null_stderr: bool,
- ) -> io::Result<Process> {
- let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
- let child = JodChild(mk_child(&path, args, null_stderr)?);
+ fn run(path: AbsPathBuf, null_stderr: bool) -> io::Result<Process> {
+ let child = JodChild(mk_child(&path, null_stderr)?);
Ok(Process { child })
}
@@ -115,13 +110,8 @@ impl Process {
}
}
-fn mk_child(
- path: &AbsPath,
- args: impl IntoIterator<Item = impl AsRef<OsStr>>,
- null_stderr: bool,
-) -> io::Result<Child> {
+fn mk_child(path: &AbsPath, null_stderr: bool) -> io::Result<Child> {
Command::new(path.as_os_str())
- .args(args)
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
index cf637ec35..13f67a012 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
@@ -122,7 +122,7 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
// https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632
let snappy_portion = match version {
5 | 6 => &dot_rustc[8..],
- 7 => {
+ 7 | 8 => {
let len_bytes = &dot_rustc[8..12];
let data_len = u32::from_be_bytes(len_bytes.try_into().unwrap()) as usize;
&dot_rustc[12..data_len + 12]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
index c402bc022..8f03c6ec7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
@@ -10,6 +10,7 @@ rust-version.workspace = true
[dependencies]
proc-macro-srv.workspace = true
+proc-macro-api.workspace = true
[features]
sysroot-abi = ["proc-macro-srv/sysroot-abi"]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index ac9fa9f5a..bece19518 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -1,6 +1,6 @@
//! A standalone binary for `proc-macro-srv`.
-
-use proc_macro_srv::cli;
+//! Driver for proc macro server
+use std::io;
fn main() -> std::io::Result<()> {
let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
@@ -15,5 +15,37 @@ fn main() -> std::io::Result<()> {
}
}
- cli::run()
+ run()
+}
+
+#[cfg(not(feature = "sysroot-abi"))]
+fn run() -> io::Result<()> {
+ panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled");
+}
+
+#[cfg(feature = "sysroot-abi")]
+fn run() -> io::Result<()> {
+ use proc_macro_api::msg::{self, Message};
+
+ let read_request = |buf: &mut String| msg::Request::read(&mut io::stdin().lock(), buf);
+
+ let write_response = |msg: msg::Response| msg.write(&mut io::stdout().lock());
+
+ let mut srv = proc_macro_srv::ProcMacroSrv::default();
+ let mut buf = String::new();
+
+ while let Some(req) = read_request(&mut buf)? {
+ let res = match req {
+ msg::Request::ListMacros { dylib_path } => {
+ msg::Response::ListMacros(srv.list_macros(&dylib_path))
+ }
+ msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
+ msg::Request::ApiVersionCheck {} => {
+ msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
+ }
+ };
+ write_response(res)?
+ }
+
+ Ok(())
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index f7f07cfcb..d5eb157bf 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -22,6 +22,7 @@ object = { version = "0.30.2", default-features = false, features = [
libloading = "0.7.3"
memmap2 = "0.5.4"
+stdx.workspace = true
tt.workspace = true
mbe.workspace = true
paths.workspace = true
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
deleted file mode 100644
index 93805c893..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-//! Macro ABI for version 1.63 of rustc
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
-use libloading::Library;
-use proc_macro_api::ProcMacroKind;
-
-use super::tt;
-use super::PanicMessage;
-
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
- exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
-}
-
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
- fn from(p: proc_macro::bridge::PanicMessage) -> Self {
- Self { message: p.as_str().map(|s| s.to_string()) }
- }
-}
-
-impl Abi {
- pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
- let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
- lib.get(symbol_name.as_bytes())?;
- Ok(Self { exported_macros: macros.to_vec() })
- }
-
- pub fn expand(
- &self,
- macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- let parsed_body = TokenStream::with_subtree(macro_body.clone());
-
- let parsed_attributes =
- attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone()));
-
- for proc_macro in &self.exported_macros {
- match proc_macro {
- proc_macro::bridge::client::ProcMacro::CustomDerive {
- trait_name, client, ..
- } if *trait_name == macro_name => {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- proc_macro::bridge::client::ProcMacro::Bang { name, client }
- if *name == macro_name =>
- {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- proc_macro::bridge::client::ProcMacro::Attr { name, client }
- if *name == macro_name =>
- {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_attributes,
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- _ => continue,
- }
- }
-
- Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
- }
-
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- self.exported_macros
- .iter()
- .map(|proc_macro| match proc_macro {
- proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
- (trait_name.to_string(), ProcMacroKind::CustomDerive)
- }
- proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
- (name.to_string(), ProcMacroKind::FuncLike)
- }
- proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
- (name.to_string(), ProcMacroKind::Attr)
- }
- })
- .collect()
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
deleted file mode 100644
index 48030f8d8..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-//! Buffer management for same-process client<->server communication.
-
-use std::io::{self, Write};
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::slice;
-
-#[repr(C)]
-pub struct Buffer {
- data: *mut u8,
- len: usize,
- capacity: usize,
- reserve: extern "C" fn(Buffer, usize) -> Buffer,
- drop: extern "C" fn(Buffer),
-}
-
-unsafe impl Sync for Buffer {}
-unsafe impl Send for Buffer {}
-
-impl Default for Buffer {
- #[inline]
- fn default() -> Self {
- Self::from(vec![])
- }
-}
-
-impl Deref for Buffer {
- type Target = [u8];
- #[inline]
- fn deref(&self) -> &[u8] {
- unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }
- }
-}
-
-impl DerefMut for Buffer {
- #[inline]
- fn deref_mut(&mut self) -> &mut [u8] {
- unsafe { slice::from_raw_parts_mut(self.data, self.len) }
- }
-}
-
-impl Buffer {
- #[inline]
- pub(super) fn new() -> Self {
- Self::default()
- }
-
- #[inline]
- pub(super) fn clear(&mut self) {
- self.len = 0;
- }
-
- #[inline]
- pub(super) fn take(&mut self) -> Self {
- mem::take(self)
- }
-
- // We have the array method separate from extending from a slice. This is
- // because in the case of small arrays, codegen can be more efficient
- // (avoiding a memmove call). With extend_from_slice, LLVM at least
- // currently is not able to make that optimization.
- #[inline]
- pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) {
- if xs.len() > (self.capacity - self.len) {
- let b = self.take();
- *self = (b.reserve)(b, xs.len());
- }
- unsafe {
- xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
- self.len += xs.len();
- }
- }
-
- #[inline]
- pub(super) fn extend_from_slice(&mut self, xs: &[u8]) {
- if xs.len() > (self.capacity - self.len) {
- let b = self.take();
- *self = (b.reserve)(b, xs.len());
- }
- unsafe {
- xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
- self.len += xs.len();
- }
- }
-
- #[inline]
- pub(super) fn push(&mut self, v: u8) {
- // The code here is taken from Vec::push, and we know that reserve()
- // will panic if we're exceeding isize::MAX bytes and so there's no need
- // to check for overflow.
- if self.len == self.capacity {
- let b = self.take();
- *self = (b.reserve)(b, 1);
- }
- unsafe {
- *self.data.add(self.len) = v;
- self.len += 1;
- }
- }
-}
-
-impl Write for Buffer {
- #[inline]
- fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
- self.extend_from_slice(xs);
- Ok(xs.len())
- }
-
- #[inline]
- fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
- self.extend_from_slice(xs);
- Ok(())
- }
-
- #[inline]
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl Drop for Buffer {
- #[inline]
- fn drop(&mut self) {
- let b = self.take();
- (b.drop)(b);
- }
-}
-
-impl From<Vec<u8>> for Buffer {
- fn from(mut v: Vec<u8>) -> Self {
- let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity());
- mem::forget(v);
-
- // This utility function is nested in here because it can *only*
- // be safely called on `Buffer`s created by *this* `proc_macro`.
- fn to_vec(b: Buffer) -> Vec<u8> {
- unsafe {
- let Buffer { data, len, capacity, .. } = b;
- mem::forget(b);
- Vec::from_raw_parts(data, len, capacity)
- }
- }
-
- extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer {
- let mut v = to_vec(b);
- v.reserve(additional);
- Buffer::from(v)
- }
-
- extern "C" fn drop(b: Buffer) {
- mem::drop(to_vec(b));
- }
-
- Buffer { data, len, capacity, reserve, drop }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
deleted file mode 100644
index b346c2c18..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
+++ /dev/null
@@ -1,510 +0,0 @@
-//! Client-side types.
-
-use super::*;
-
-use std::marker::PhantomData;
-
-macro_rules! define_handles {
- (
- 'owned: $($oty:ident,)*
- 'interned: $($ity:ident,)*
- ) => {
- #[repr(C)]
- #[allow(non_snake_case)]
- pub struct HandleCounters {
- $($oty: AtomicUsize,)*
- $($ity: AtomicUsize,)*
- }
-
- impl HandleCounters {
- // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
- // a wrapper `fn` pointer, once `const fn` can reference `static`s.
- extern "C" fn get() -> &'static Self {
- static COUNTERS: HandleCounters = HandleCounters {
- $($oty: AtomicUsize::new(1),)*
- $($ity: AtomicUsize::new(1),)*
- };
- &COUNTERS
- }
- }
-
- // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
- #[repr(C)]
- #[allow(non_snake_case)]
- pub(super) struct HandleStore<S: server::Types> {
- $($oty: handle::OwnedStore<S::$oty>,)*
- $($ity: handle::InternedStore<S::$ity>,)*
- }
-
- impl<S: server::Types> HandleStore<S> {
- pub(super) fn new(handle_counters: &'static HandleCounters) -> Self {
- HandleStore {
- $($oty: handle::OwnedStore::new(&handle_counters.$oty),)*
- $($ity: handle::InternedStore::new(&handle_counters.$ity),)*
- }
- }
- }
-
- $(
- #[repr(C)]
- pub(crate) struct $oty {
- handle: handle::Handle,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
- // way of doing this, but that requires unstable features.
- // rust-analyzer uses this code and avoids unstable features.
- _marker: PhantomData<*mut ()>,
- }
-
- // Forward `Drop::drop` to the inherent `drop` method.
- impl Drop for $oty {
- fn drop(&mut self) {
- $oty {
- handle: self.handle,
- _marker: PhantomData,
- }.drop();
- }
- }
-
- impl<S> Encode<S> for $oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- let handle = self.handle;
- mem::forget(self);
- handle.encode(w, s);
- }
- }
-
- impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$oty, $oty>
- {
- fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
- s.$oty.take(handle::Handle::decode(r, &mut ()))
- }
- }
-
- impl<S> Encode<S> for &$oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<'s, S: server::Types> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>>
- for &'s Marked<S::$oty, $oty>
- {
- fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self {
- &s.$oty[handle::Handle::decode(r, &mut ())]
- }
- }
-
- impl<S> Encode<S> for &mut $oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>>
- for &'s mut Marked<S::$oty, $oty>
- {
- fn decode(
- r: &mut Reader<'_>,
- s: &'s mut HandleStore<server::MarkedTypes<S>>
- ) -> Self {
- &mut s.$oty[handle::Handle::decode(r, &mut ())]
- }
- }
-
- impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$oty, $oty>
- {
- fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
- s.$oty.alloc(self).encode(w, s);
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $oty {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- $oty {
- handle: handle::Handle::decode(r, s),
- _marker: PhantomData,
- }
- }
- }
- )*
-
- $(
- #[repr(C)]
- #[derive(Copy, Clone, PartialEq, Eq, Hash)]
- pub(crate) struct $ity {
- handle: handle::Handle,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
- // way of doing this, but that requires unstable features.
- // rust-analyzer uses this code and avoids unstable features.
- _marker: PhantomData<*mut ()>,
- }
-
- impl<S> Encode<S> for $ity {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$ity, $ity>
- {
- fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
- s.$ity.copy(handle::Handle::decode(r, &mut ()))
- }
- }
-
- impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$ity, $ity>
- {
- fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
- s.$ity.alloc(self).encode(w, s);
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $ity {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- $ity {
- handle: handle::Handle::decode(r, s),
- _marker: PhantomData,
- }
- }
- }
- )*
- }
-}
-define_handles! {
- 'owned:
- FreeFunctions,
- TokenStream,
- Group,
- Literal,
- SourceFile,
- MultiSpan,
- Diagnostic,
-
- 'interned:
- Punct,
- Ident,
- Span,
-}
-
-// FIXME(eddyb) generate these impls by pattern-matching on the
-// names of methods - also could use the presence of `fn drop`
-// to distinguish between 'owned and 'interned, above.
-// Alternatively, special "modes" could be listed of types in with_api
-// instead of pattern matching on methods, here and in server decl.
-
-impl Clone for TokenStream {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl Clone for Group {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl Clone for Literal {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl fmt::Debug for Literal {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Literal")
- // format the kind without quotes, as in `kind: Float`
- .field("kind", &format_args!("{}", &self.debug_kind()))
- .field("symbol", &self.symbol())
- // format `Some("...")` on one line even in {:#?} mode
- .field("suffix", &format_args!("{:?}", &self.suffix()))
- .field("span", &self.span())
- .finish()
- }
-}
-
-impl Clone for SourceFile {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl fmt::Debug for Span {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(&self.debug())
- }
-}
-
-macro_rules! define_client_side {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
- }),* $(,)?) => {
- $(impl $name {
- $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
- Bridge::with(|bridge| {
- let mut buf = bridge.cached_buffer.take();
-
- buf.clear();
- api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
- reverse_encode!(buf; $($arg),*);
-
- buf = bridge.dispatch.call(buf);
-
- let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
-
- bridge.cached_buffer = buf;
-
- r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
- })
- })*
- })*
- }
-}
-with_api!(self, self, define_client_side);
-
-enum BridgeState<'a> {
- /// No server is currently connected to this client.
- NotConnected,
-
- /// A server is connected and available for requests.
- Connected(Bridge<'a>),
-
- /// Access to the bridge is being exclusively acquired
- /// (e.g., during `BridgeState::with`).
- InUse,
-}
-
-enum BridgeStateL {}
-
-impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL {
- type Out = BridgeState<'a>;
-}
-
-thread_local! {
- static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> =
- scoped_cell::ScopedCell::new(BridgeState::NotConnected);
-}
-
-impl BridgeState<'_> {
- /// Take exclusive control of the thread-local
- /// `BridgeState`, and pass it to `f`, mutably.
- /// The state will be restored after `f` exits, even
- /// by panic, including modifications made to it by `f`.
- ///
- /// N.B., while `f` is running, the thread-local state
- /// is `BridgeState::InUse`.
- fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R {
- BRIDGE_STATE.with(|state| {
- state.replace(BridgeState::InUse, |mut state| {
- // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone
- f(&mut state)
- })
- })
- }
-}
-
-impl Bridge<'_> {
- pub(crate) fn is_available() -> bool {
- BridgeState::with(|state| match state {
- BridgeState::Connected(_) | BridgeState::InUse => true,
- BridgeState::NotConnected => false,
- })
- }
-
- fn enter<R>(self, f: impl FnOnce() -> R) -> R {
- let force_show_panics = self.force_show_panics;
- // Hide the default panic output within `proc_macro` expansions.
- // NB. the server can't do this because it may use a different libstd.
- static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
- HIDE_PANICS_DURING_EXPANSION.call_once(|| {
- let prev = panic::take_hook();
- panic::set_hook(Box::new(move |info| {
- let show = BridgeState::with(|state| match state {
- BridgeState::NotConnected => true,
- BridgeState::Connected(_) | BridgeState::InUse => force_show_panics,
- });
- if show {
- prev(info)
- }
- }));
- });
-
- BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
- }
-
- fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
- BridgeState::with(|state| match state {
- BridgeState::NotConnected => {
- panic!("procedural macro API is used outside of a procedural macro");
- }
- BridgeState::InUse => {
- panic!("procedural macro API is used while it's already in use");
- }
- BridgeState::Connected(bridge) => f(bridge),
- })
- }
-}
-
-/// A client-side RPC entry-point, which may be using a different `proc_macro`
-/// from the one used by the server, but can be invoked compatibly.
-///
-/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
-/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
-/// do not themselves participate in ABI, at all, only facilitate type-checking.
-///
-/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
-/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
-/// indicating that the RPC input and output will be serialized token streams,
-/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
-#[repr(C)]
-pub struct Client<I, O> {
- // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
- // a wrapper `fn` pointer, once `const fn` can reference `static`s.
- pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters,
-
- pub(super) run: extern "C" fn(Bridge<'_>) -> Buffer,
-
- pub(super) _marker: PhantomData<fn(I) -> O>,
-}
-
-impl<I, O> Copy for Client<I, O> {}
-impl<I, O> Clone for Client<I, O> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-/// Client-side helper for handling client panics, entering the bridge,
-/// deserializing input and serializing output.
-// FIXME(eddyb) maybe replace `Bridge::enter` with this?
-fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
- mut bridge: Bridge<'_>,
- f: impl FnOnce(A) -> R,
-) -> Buffer {
- // The initial `cached_buffer` contains the input.
- let mut buf = bridge.cached_buffer.take();
-
- panic::catch_unwind(panic::AssertUnwindSafe(|| {
- bridge.enter(|| {
- let reader = &mut &buf[..];
- let input = A::decode(reader, &mut ());
-
- // Put the `cached_buffer` back in the `Bridge`, for requests.
- Bridge::with(|bridge| bridge.cached_buffer = buf.take());
-
- let output = f(input);
-
- // Take the `cached_buffer` back out, for the output value.
- buf = Bridge::with(|bridge| bridge.cached_buffer.take());
-
- // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
- // from encoding a panic (`Err(e: PanicMessage)`) to avoid
- // having handles outside the `bridge.enter(|| ...)` scope, and
- // to catch panics that could happen while encoding the success.
- //
- // Note that panics should be impossible beyond this point, but
- // this is defensively trying to avoid any accidental panicking
- // reaching the `extern "C"` (which should `abort` but might not
- // at the moment, so this is also potentially preventing UB).
- buf.clear();
- Ok::<_, ()>(output).encode(&mut buf, &mut ());
- })
- }))
- .map_err(PanicMessage::from)
- .unwrap_or_else(|e| {
- buf.clear();
- Err::<(), _>(e).encode(&mut buf, &mut ());
- });
- buf
-}
-
-impl Client<super::super::TokenStream, super::super::TokenStream> {
- pub const fn expand1(
- f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- Client {
- get_handle_counters: HandleCounters::get,
- run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
- run_client(bridge, |input| f(super::super::TokenStream(input)).0)
- }),
- _marker: PhantomData,
- }
- }
-}
-
-impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> {
- pub const fn expand2(
- f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
- + Copy,
- ) -> Self {
- Client {
- get_handle_counters: HandleCounters::get,
- run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
- run_client(bridge, |(input, input2)| {
- f(super::super::TokenStream(input), super::super::TokenStream(input2)).0
- })
- }),
- _marker: PhantomData,
- }
- }
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub enum ProcMacro {
- CustomDerive {
- trait_name: &'static str,
- attributes: &'static [&'static str],
- client: Client<super::super::TokenStream, super::super::TokenStream>,
- },
-
- Attr {
- name: &'static str,
- client: Client<
- (super::super::TokenStream, super::super::TokenStream),
- super::super::TokenStream,
- >,
- },
-
- Bang {
- name: &'static str,
- client: Client<super::super::TokenStream, super::super::TokenStream>,
- },
-}
-
-impl ProcMacro {
- pub fn name(&self) -> &'static str {
- match self {
- ProcMacro::CustomDerive { trait_name, .. } => trait_name,
- ProcMacro::Attr { name, .. } => name,
- ProcMacro::Bang { name, .. } => name,
- }
- }
-
- pub const fn custom_derive(
- trait_name: &'static str,
- attributes: &'static [&'static str],
- expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
- }
-
- pub const fn attr(
- name: &'static str,
- expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
- + Copy,
- ) -> Self {
- ProcMacro::Attr { name, client: Client::expand2(expand) }
- }
-
- pub const fn bang(
- name: &'static str,
- expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- ProcMacro::Bang { name, client: Client::expand1(expand) }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
deleted file mode 100644
index d371ae3ce..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
-
-use std::marker::PhantomData;
-
-#[repr(C)]
-pub struct Closure<'a, A, R> {
- call: unsafe extern "C" fn(*mut Env, A) -> R,
- env: *mut Env,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
- // this, but that requires unstable features. rust-analyzer uses this code
- // and avoids unstable features.
- //
- // The `'a` lifetime parameter represents the lifetime of `Env`.
- _marker: PhantomData<*mut &'a mut ()>,
-}
-
-struct Env;
-
-impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> {
- fn from(f: &'a mut F) -> Self {
- unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R {
- (*(env as *mut _ as *mut F))(arg)
- }
- Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
- }
-}
-
-impl<'a, A, R> Closure<'a, A, R> {
- pub fn call(&mut self, arg: A) -> R {
- unsafe { (self.call)(self.env, arg) }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
deleted file mode 100644
index c219a9465..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-//! Server-side handles and storage for per-handle data.
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::{BuildHasher, Hash};
-use std::num::NonZeroU32;
-use std::ops::{Index, IndexMut};
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-pub(super) type Handle = NonZeroU32;
-
-/// A store that associates values of type `T` with numeric handles. A value can
-/// be looked up using its handle.
-pub(super) struct OwnedStore<T: 'static> {
- counter: &'static AtomicUsize,
- data: BTreeMap<Handle, T>,
-}
-
-impl<T> OwnedStore<T> {
- pub(super) fn new(counter: &'static AtomicUsize) -> Self {
- // Ensure the handle counter isn't 0, which would panic later,
- // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`.
- assert_ne!(counter.load(Ordering::SeqCst), 0);
-
- OwnedStore { counter, data: BTreeMap::new() }
- }
-}
-
-impl<T> OwnedStore<T> {
- pub(super) fn alloc(&mut self, x: T) -> Handle {
- let counter = self.counter.fetch_add(1, Ordering::SeqCst);
- let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed");
- assert!(self.data.insert(handle, x).is_none());
- handle
- }
-
- pub(super) fn take(&mut self, h: Handle) -> T {
- self.data.remove(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-impl<T> Index<Handle> for OwnedStore<T> {
- type Output = T;
- fn index(&self, h: Handle) -> &T {
- self.data.get(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-impl<T> IndexMut<Handle> for OwnedStore<T> {
- fn index_mut(&mut self, h: Handle) -> &mut T {
- self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement
-// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`).
-#[derive(Clone)]
-struct NonRandomState;
-
-impl BuildHasher for NonRandomState {
- type Hasher = std::collections::hash_map::DefaultHasher;
- #[inline]
- fn build_hasher(&self) -> Self::Hasher {
- Self::Hasher::new()
- }
-}
-
-/// Like `OwnedStore`, but avoids storing any value more than once.
-pub(super) struct InternedStore<T: 'static> {
- owned: OwnedStore<T>,
- interner: HashMap<T, Handle, NonRandomState>,
-}
-
-impl<T: Copy + Eq + Hash> InternedStore<T> {
- pub(super) fn new(counter: &'static AtomicUsize) -> Self {
- InternedStore {
- owned: OwnedStore::new(counter),
- interner: HashMap::with_hasher(NonRandomState),
- }
- }
-
- pub(super) fn alloc(&mut self, x: T) -> Handle {
- let owned = &mut self.owned;
- *self.interner.entry(x).or_insert_with(|| owned.alloc(x))
- }
-
- pub(super) fn copy(&mut self, h: Handle) -> T {
- self.owned[h]
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
deleted file mode 100644
index 4967da493..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-//! Internal interface for communicating between a `proc_macro` client
-//! (a proc macro crate) and a `proc_macro` server (a compiler front-end).
-//!
-//! Serialization (with C ABI buffers) and unique integer handles are employed
-//! to allow safely interfacing between two copies of `proc_macro` built
-//! (from the same source) by different compilers with potentially mismatching
-//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap).
-
-#![deny(unsafe_code)]
-
-pub use super::{Delimiter, Level, LineColumn, Spacing};
-use std::fmt;
-use std::hash::Hash;
-use std::marker;
-use std::mem;
-use std::ops::Bound;
-use std::panic;
-use std::sync::atomic::AtomicUsize;
-use std::sync::Once;
-use std::thread;
-
-/// Higher-order macro describing the server RPC API, allowing automatic
-/// generation of type-safe Rust APIs, both client-side and server-side.
-///
-/// `with_api!(MySelf, my_self, my_macro)` expands to:
-/// ```rust,ignore (pseudo-code)
-/// my_macro! {
-/// // ...
-/// Literal {
-/// // ...
-/// fn character(ch: char) -> MySelf::Literal;
-/// // ...
-/// fn span(my_self: &MySelf::Literal) -> MySelf::Span;
-/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
-/// },
-/// // ...
-/// }
-/// ```
-///
-/// The first two arguments serve to customize the arguments names
-/// and argument/return types, to enable several different usecases:
-///
-/// If `my_self` is just `self`, then each `fn` signature can be used
-/// as-is for a method. If it's anything else (`self_` in practice),
-/// then the signatures don't have a special `self` argument, and
-/// can, therefore, have a different one introduced.
-///
-/// If `MySelf` is just `Self`, then the types are only valid inside
-/// a trait or a trait impl, where the trait has associated types
-/// for each of the API types. If non-associated types are desired,
-/// a module name (`self` in practice) can be used instead of `Self`.
-macro_rules! with_api {
- ($S:ident, $self:ident, $m:ident) => {
- $m! {
- FreeFunctions {
- fn drop($self: $S::FreeFunctions);
- fn track_env_var(var: &str, value: Option<&str>);
- fn track_path(path: &str);
- },
- TokenStream {
- fn drop($self: $S::TokenStream);
- fn clone($self: &$S::TokenStream) -> $S::TokenStream;
- fn is_empty($self: &$S::TokenStream) -> bool;
- fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
- fn from_str(src: &str) -> $S::TokenStream;
- fn to_string($self: &$S::TokenStream) -> String;
- fn from_token_tree(
- tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>,
- ) -> $S::TokenStream;
- fn concat_trees(
- base: Option<$S::TokenStream>,
- trees: Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>,
- ) -> $S::TokenStream;
- fn concat_streams(
- base: Option<$S::TokenStream>,
- streams: Vec<$S::TokenStream>,
- ) -> $S::TokenStream;
- fn into_trees(
- $self: $S::TokenStream
- ) -> Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>;
- },
- Group {
- fn drop($self: $S::Group);
- fn clone($self: &$S::Group) -> $S::Group;
- fn new(delimiter: Delimiter, stream: Option<$S::TokenStream>) -> $S::Group;
- fn delimiter($self: &$S::Group) -> Delimiter;
- fn stream($self: &$S::Group) -> $S::TokenStream;
- fn span($self: &$S::Group) -> $S::Span;
- fn span_open($self: &$S::Group) -> $S::Span;
- fn span_close($self: &$S::Group) -> $S::Span;
- fn set_span($self: &mut $S::Group, span: $S::Span);
- },
- Punct {
- fn new(ch: char, spacing: Spacing) -> $S::Punct;
- fn as_char($self: $S::Punct) -> char;
- fn spacing($self: $S::Punct) -> Spacing;
- fn span($self: $S::Punct) -> $S::Span;
- fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
- },
- Ident {
- fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
- fn span($self: $S::Ident) -> $S::Span;
- fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
- },
- Literal {
- fn drop($self: $S::Literal);
- fn clone($self: &$S::Literal) -> $S::Literal;
- fn from_str(s: &str) -> Result<$S::Literal, ()>;
- fn to_string($self: &$S::Literal) -> String;
- fn debug_kind($self: &$S::Literal) -> String;
- fn symbol($self: &$S::Literal) -> String;
- fn suffix($self: &$S::Literal) -> Option<String>;
- fn integer(n: &str) -> $S::Literal;
- fn typed_integer(n: &str, kind: &str) -> $S::Literal;
- fn float(n: &str) -> $S::Literal;
- fn f32(n: &str) -> $S::Literal;
- fn f64(n: &str) -> $S::Literal;
- fn string(string: &str) -> $S::Literal;
- fn character(ch: char) -> $S::Literal;
- fn byte_string(bytes: &[u8]) -> $S::Literal;
- fn span($self: &$S::Literal) -> $S::Span;
- fn set_span($self: &mut $S::Literal, span: $S::Span);
- fn subspan(
- $self: &$S::Literal,
- start: Bound<usize>,
- end: Bound<usize>,
- ) -> Option<$S::Span>;
- },
- SourceFile {
- fn drop($self: $S::SourceFile);
- fn clone($self: &$S::SourceFile) -> $S::SourceFile;
- fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
- fn path($self: &$S::SourceFile) -> String;
- fn is_real($self: &$S::SourceFile) -> bool;
- },
- MultiSpan {
- fn drop($self: $S::MultiSpan);
- fn new() -> $S::MultiSpan;
- fn push($self: &mut $S::MultiSpan, span: $S::Span);
- },
- Diagnostic {
- fn drop($self: $S::Diagnostic);
- fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
- fn sub(
- $self: &mut $S::Diagnostic,
- level: Level,
- msg: &str,
- span: $S::MultiSpan,
- );
- fn emit($self: $S::Diagnostic);
- },
- Span {
- fn debug($self: $S::Span) -> String;
- fn def_site() -> $S::Span;
- fn call_site() -> $S::Span;
- fn mixed_site() -> $S::Span;
- fn source_file($self: $S::Span) -> $S::SourceFile;
- fn parent($self: $S::Span) -> Option<$S::Span>;
- fn source($self: $S::Span) -> $S::Span;
- fn start($self: $S::Span) -> LineColumn;
- fn end($self: $S::Span) -> LineColumn;
- fn before($self: $S::Span) -> $S::Span;
- fn after($self: $S::Span) -> $S::Span;
- fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
- fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
- fn source_text($self: $S::Span) -> Option<String>;
- fn save_span($self: $S::Span) -> usize;
- fn recover_proc_macro_span(id: usize) -> $S::Span;
- },
- }
- };
-}
-
-// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
-// to match the ordering in `reverse_decode`.
-macro_rules! reverse_encode {
- ($writer:ident;) => {};
- ($writer:ident; $first:ident $(, $rest:ident)*) => {
- reverse_encode!($writer; $($rest),*);
- $first.encode(&mut $writer, &mut ());
- }
-}
-
-// FIXME(eddyb) this calls `decode` for each argument, but in reverse,
-// to avoid borrow conflicts from borrows started by `&mut` arguments.
-macro_rules! reverse_decode {
- ($reader:ident, $s:ident;) => {};
- ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => {
- reverse_decode!($reader, $s; $($rest: $rest_ty),*);
- let $first = <$first_ty>::decode(&mut $reader, $s);
- }
-}
-
-#[allow(unsafe_code)]
-mod buffer;
-#[forbid(unsafe_code)]
-pub mod client;
-#[allow(unsafe_code)]
-mod closure;
-#[forbid(unsafe_code)]
-mod handle;
-#[macro_use]
-#[forbid(unsafe_code)]
-mod rpc;
-#[allow(unsafe_code)]
-mod scoped_cell;
-#[allow(unsafe_code)]
-mod selfless_reify;
-#[forbid(unsafe_code)]
-pub mod server;
-
-use buffer::Buffer;
-pub use rpc::PanicMessage;
-use rpc::{Decode, DecodeMut, Encode, Reader, Writer};
-
-/// An active connection between a server and a client.
-/// The server creates the bridge (`Bridge::run_server` in `server.rs`),
-/// then passes it to the client through the function pointer in the `run`
-/// field of `client::Client`. The client holds its copy of the `Bridge`
-/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`).
-#[repr(C)]
-pub struct Bridge<'a> {
- /// Reusable buffer (only `clear`-ed, never shrunk), primarily
- /// used for making requests, but also for passing input to client.
- cached_buffer: Buffer,
-
- /// Server-side function that the client uses to make requests.
- dispatch: closure::Closure<'a, Buffer, Buffer>,
-
- /// If 'true', always invoke the default panic hook
- force_show_panics: bool,
-
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
- // this, but that requires unstable features. rust-analyzer uses this code
- // and avoids unstable features.
- _marker: marker::PhantomData<*mut ()>,
-}
-
-#[forbid(unsafe_code)]
-#[allow(non_camel_case_types)]
-mod api_tags {
- use super::rpc::{DecodeMut, Encode, Reader, Writer};
-
- macro_rules! declare_tags {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
- }),* $(,)?) => {
- $(
- pub(super) enum $name {
- $($method),*
- }
- rpc_encode_decode!(enum $name { $($method),* });
- )*
-
- pub(super) enum Method {
- $($name($name)),*
- }
- rpc_encode_decode!(enum Method { $($name(m)),* });
- }
- }
- with_api!(self, self, declare_tags);
-}
-
-/// Helper to wrap associated types to allow trait impl dispatch.
-/// That is, normally a pair of impls for `T::Foo` and `T::Bar`
-/// can overlap, but if the impls are, instead, on types like
-/// `Marked<T::Foo, Foo>` and `Marked<T::Bar, Bar>`, they can't.
-trait Mark {
- type Unmarked;
- fn mark(unmarked: Self::Unmarked) -> Self;
-}
-
-/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
-trait Unmark {
- type Unmarked;
- fn unmark(self) -> Self::Unmarked;
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-struct Marked<T, M> {
- value: T,
- _marker: marker::PhantomData<M>,
-}
-
-impl<T, M> Mark for Marked<T, M> {
- type Unmarked = T;
- fn mark(unmarked: Self::Unmarked) -> Self {
- Marked { value: unmarked, _marker: marker::PhantomData }
- }
-}
-impl<T, M> Unmark for Marked<T, M> {
- type Unmarked = T;
- fn unmark(self) -> Self::Unmarked {
- self.value
- }
-}
-impl<'a, T, M> Unmark for &'a Marked<T, M> {
- type Unmarked = &'a T;
- fn unmark(self) -> Self::Unmarked {
- &self.value
- }
-}
-impl<'a, T, M> Unmark for &'a mut Marked<T, M> {
- type Unmarked = &'a mut T;
- fn unmark(self) -> Self::Unmarked {
- &mut self.value
- }
-}
-
-impl<T: Mark> Mark for Vec<T> {
- type Unmarked = Vec<T::Unmarked>;
- fn mark(unmarked: Self::Unmarked) -> Self {
- // Should be a no-op due to std's in-place collect optimizations.
- unmarked.into_iter().map(T::mark).collect()
- }
-}
-impl<T: Unmark> Unmark for Vec<T> {
- type Unmarked = Vec<T::Unmarked>;
- fn unmark(self) -> Self::Unmarked {
- // Should be a no-op due to std's in-place collect optimizations.
- self.into_iter().map(T::unmark).collect()
- }
-}
-
-macro_rules! mark_noop {
- ($($ty:ty),* $(,)?) => {
- $(
- impl Mark for $ty {
- type Unmarked = Self;
- fn mark(unmarked: Self::Unmarked) -> Self {
- unmarked
- }
- }
- impl Unmark for $ty {
- type Unmarked = Self;
- fn unmark(self) -> Self::Unmarked {
- self
- }
- }
- )*
- }
-}
-mark_noop! {
- (),
- bool,
- char,
- &'_ [u8],
- &'_ str,
- String,
- usize,
- Delimiter,
- Level,
- LineColumn,
- Spacing,
-}
-
-rpc_encode_decode!(
- enum Delimiter {
- Parenthesis,
- Brace,
- Bracket,
- None,
- }
-);
-rpc_encode_decode!(
- enum Level {
- Error,
- Warning,
- Note,
- Help,
- }
-);
-rpc_encode_decode!(struct LineColumn { line, column });
-rpc_encode_decode!(
- enum Spacing {
- Alone,
- Joint,
- }
-);
-
-macro_rules! mark_compound {
- (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => {
- impl<$($T: Mark),+> Mark for $name <$($T),+> {
- type Unmarked = $name <$($T::Unmarked),+>;
- fn mark(unmarked: Self::Unmarked) -> Self {
- match unmarked {
- $($name::$variant $(($field))? => {
- $name::$variant $((Mark::mark($field)))?
- })*
- }
- }
- }
-
- impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
- type Unmarked = $name <$($T::Unmarked),+>;
- fn unmark(self) -> Self::Unmarked {
- match self {
- $($name::$variant $(($field))? => {
- $name::$variant $((Unmark::unmark($field)))?
- })*
- }
- }
- }
- }
-}
-
-macro_rules! compound_traits {
- ($($t:tt)*) => {
- rpc_encode_decode!($($t)*);
- mark_compound!($($t)*);
- };
-}
-
-compound_traits!(
- enum Bound<T> {
- Included(x),
- Excluded(x),
- Unbounded,
- }
-);
-
-compound_traits!(
- enum Option<T> {
- Some(t),
- None,
- }
-);
-
-compound_traits!(
- enum Result<T, E> {
- Ok(t),
- Err(e),
- }
-);
-
-#[derive(Clone)]
-pub enum TokenTree<G, P, I, L> {
- Group(G),
- Punct(P),
- Ident(I),
- Literal(L),
-}
-
-compound_traits!(
- enum TokenTree<G, P, I, L> {
- Group(tt),
- Punct(tt),
- Ident(tt),
- Literal(tt),
- }
-);
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
deleted file mode 100644
index e9d7a46c0..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
+++ /dev/null
@@ -1,304 +0,0 @@
-//! Serialization for client-server communication.
-
-use std::any::Any;
-use std::char;
-use std::io::Write;
-use std::num::NonZeroU32;
-use std::str;
-
-pub(super) type Writer = super::buffer::Buffer;
-
-pub(super) trait Encode<S>: Sized {
- fn encode(self, w: &mut Writer, s: &mut S);
-}
-
-pub(super) type Reader<'a> = &'a [u8];
-
-pub(super) trait Decode<'a, 's, S>: Sized {
- fn decode(r: &mut Reader<'a>, s: &'s S) -> Self;
-}
-
-pub(super) trait DecodeMut<'a, 's, S>: Sized {
- fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
-}
-
-macro_rules! rpc_encode_decode {
- (le $ty:ty) => {
- impl<S> Encode<S> for $ty {
- fn encode(self, w: &mut Writer, _: &mut S) {
- w.extend_from_array(&self.to_le_bytes());
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $ty {
- fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
- const N: usize = ::std::mem::size_of::<$ty>();
-
- let mut bytes = [0; N];
- bytes.copy_from_slice(&r[..N]);
- *r = &r[N..];
-
- Self::from_le_bytes(bytes)
- }
- }
- };
- (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
- impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
- fn encode(self, w: &mut Writer, s: &mut S) {
- $(self.$field.encode(w, s);)*
- }
- }
-
- impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
- for $name $(<$($T),+>)?
- {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- $name {
- $($field: DecodeMut::decode(r, s)),*
- }
- }
- }
- };
- (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
- impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
- fn encode(self, w: &mut Writer, s: &mut S) {
- // HACK(eddyb): `Tag` enum duplicated between the
- // two impls as there's no other place to stash it.
- #[allow(non_upper_case_globals)]
- mod tag {
- #[repr(u8)] enum Tag { $($variant),* }
-
- $(pub const $variant: u8 = Tag::$variant as u8;)*
- }
-
- match self {
- $($name::$variant $(($field))* => {
- tag::$variant.encode(w, s);
- $($field.encode(w, s);)*
- })*
- }
- }
- }
-
- impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
- for $name $(<$($T),+>)?
- {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- // HACK(eddyb): `Tag` enum duplicated between the
- // two impls as there's no other place to stash it.
- #[allow(non_upper_case_globals)]
- mod tag {
- #[repr(u8)] enum Tag { $($variant),* }
-
- $(pub const $variant: u8 = Tag::$variant as u8;)*
- }
-
- match u8::decode(r, s) {
- $(tag::$variant => {
- $(let $field = DecodeMut::decode(r, s);)*
- $name::$variant $(($field))*
- })*
- _ => unreachable!(),
- }
- }
- }
- }
-}
-
-impl<S> Encode<S> for () {
- fn encode(self, _: &mut Writer, _: &mut S) {}
-}
-
-impl<S> DecodeMut<'_, '_, S> for () {
- fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
-}
-
-impl<S> Encode<S> for u8 {
- fn encode(self, w: &mut Writer, _: &mut S) {
- w.push(self);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for u8 {
- fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
- let x = r[0];
- *r = &r[1..];
- x
- }
-}
-
-rpc_encode_decode!(le u32);
-rpc_encode_decode!(le usize);
-
-impl<S> Encode<S> for bool {
- fn encode(self, w: &mut Writer, s: &mut S) {
- (self as u8).encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for bool {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- match u8::decode(r, s) {
- 0 => false,
- 1 => true,
- _ => unreachable!(),
- }
- }
-}
-
-impl<S> Encode<S> for char {
- fn encode(self, w: &mut Writer, s: &mut S) {
- (self as u32).encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for char {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- char::from_u32(u32::decode(r, s)).unwrap()
- }
-}
-
-impl<S> Encode<S> for NonZeroU32 {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.get().encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- Self::new(u32::decode(r, s)).unwrap()
- }
-}
-
-impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.0.encode(w, s);
- self.1.encode(w, s);
- }
-}
-
-impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
- for (A, B)
-{
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- (DecodeMut::decode(r, s), DecodeMut::decode(r, s))
- }
-}
-
-impl<S> Encode<S> for &[u8] {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.len().encode(w, s);
- w.write_all(self).unwrap();
- }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- let len = usize::decode(r, s);
- let xs = &r[..len];
- *r = &r[len..];
- xs
- }
-}
-
-impl<S> Encode<S> for &str {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.as_bytes().encode(w, s);
- }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a str {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
- }
-}
-
-impl<S> Encode<S> for String {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self[..].encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for String {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- <&str>::decode(r, s).to_string()
- }
-}
-
-impl<S, T: Encode<S>> Encode<S> for Vec<T> {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.len().encode(w, s);
- for x in self {
- x.encode(w, s);
- }
- }
-}
-
-impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec<T> {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- let len = usize::decode(r, s);
- let mut vec = Vec::with_capacity(len);
- for _ in 0..len {
- vec.push(T::decode(r, s));
- }
- vec
- }
-}
-
-/// Simplified version of panic payloads, ignoring
-/// types other than `&'static str` and `String`.
-pub enum PanicMessage {
- StaticStr(&'static str),
- String(String),
- Unknown,
-}
-
-impl From<Box<dyn Any + Send>> for PanicMessage {
- fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
- if let Some(s) = payload.downcast_ref::<&'static str>() {
- return PanicMessage::StaticStr(s);
- }
- if let Ok(s) = payload.downcast::<String>() {
- return PanicMessage::String(*s);
- }
- PanicMessage::Unknown
- }
-}
-
-impl Into<Box<dyn Any + Send>> for PanicMessage {
- fn into(self) -> Box<dyn Any + Send> {
- match self {
- PanicMessage::StaticStr(s) => Box::new(s),
- PanicMessage::String(s) => Box::new(s),
- PanicMessage::Unknown => {
- struct UnknownPanicMessage;
- Box::new(UnknownPanicMessage)
- }
- }
- }
-}
-
-impl PanicMessage {
- pub fn as_str(&self) -> Option<&str> {
- match self {
- PanicMessage::StaticStr(s) => Some(s),
- PanicMessage::String(s) => Some(s),
- PanicMessage::Unknown => None,
- }
- }
-}
-
-impl<S> Encode<S> for PanicMessage {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.as_str().encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for PanicMessage {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- match Option::<String>::decode(r, s) {
- Some(s) => PanicMessage::String(s),
- None => PanicMessage::Unknown,
- }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
deleted file mode 100644
index 2cde1f65a..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-//! `Cell` variant for (scoped) existential lifetimes.
-
-use std::cell::Cell;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-
-/// Type lambda application, with a lifetime.
-#[allow(unused_lifetimes)]
-pub trait ApplyL<'a> {
- type Out;
-}
-
-/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
-pub trait LambdaL: for<'a> ApplyL<'a> {}
-
-impl<T: for<'a> ApplyL<'a>> LambdaL for T {}
-
-// HACK(eddyb) work around projection limitations with a newtype
-// FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out`
-pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
-
-impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> {
- type Target = <T as ApplyL<'b>>::Out;
- fn deref(&self) -> &Self::Target {
- self.0
- }
-}
-
-impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.0
- }
-}
-
-pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
-
-impl<T: LambdaL> ScopedCell<T> {
- pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
- ScopedCell(Cell::new(value))
- }
-
- /// Sets the value in `self` to `replacement` while
- /// running `f`, which gets the old value, mutably.
- /// The old value will be restored after `f` exits, even
- /// by panic, including modifications made to it by `f`.
- pub fn replace<'a, R>(
- &self,
- replacement: <T as ApplyL<'a>>::Out,
- f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R,
- ) -> R {
- /// Wrapper that ensures that the cell always gets filled
- /// (with the original state, optionally changed by `f`),
- /// even if `f` had panicked.
- struct PutBackOnDrop<'a, T: LambdaL> {
- cell: &'a ScopedCell<T>,
- value: Option<<T as ApplyL<'static>>::Out>,
- }
-
- impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> {
- fn drop(&mut self) {
- self.cell.0.set(self.value.take().unwrap());
- }
- }
-
- let mut put_back_on_drop = PutBackOnDrop {
- cell: self,
- value: Some(self.0.replace(unsafe {
- let erased = mem::transmute_copy(&replacement);
- mem::forget(replacement);
- erased
- })),
- };
-
- f(RefMutL(put_back_on_drop.value.as_mut().unwrap()))
- }
-
- /// Sets the value in `self` to `value` while running `f`.
- pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R {
- self.replace(value, |_| f())
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
deleted file mode 100644
index 4ee4bb87c..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! Abstraction for creating `fn` pointers from any callable that *effectively*
-//! has the equivalent of implementing `Default`, even if the compiler neither
-//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers)
-//! other than those with absolutely no captures.
-//!
-//! More specifically, for a closure-like type to be "effectively `Default`":
-//! * it must be a ZST (zero-sized type): no information contained within, so
-//! that `Default`'s return value (if it were implemented) is unambiguous
-//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar
-//! types that would make duplicating values at will unsound
-//! * combined with the ZST requirement, this confers a kind of "telecopy"
-//! ability: similar to `Copy`, but without keeping the value around, and
-//! instead "reconstructing" it (a noop given it's a ZST) when needed
-//! * it must be *provably* inhabited: no captured uninhabited types or any
-//! other types that cannot be constructed by the user of this abstraction
-//! * the proof is a value of the closure-like type itself, in a sense the
-//! "seed" for the "telecopy" process made possible by ZST + `Copy`
-//! * this requirement is the only reason an abstraction limited to a specific
-//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic
-//! at the "attempted `::default()` call" time, but that doesn't guarantee
-//! that the value can be soundly created, and attempting to use the typical
-//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type
-//! that is not proof of anything without a value (i.e. isomorphic to a
-//! newtype of the type it's trying to prove the inhabitation of)
-//!
-//! A more flexible (and safer) solution to the general problem could exist once
-//! `const`-generic parameters can have type parameters in their types:
-//!
-//! ```rust,ignore (needs future const-generics)
-//! extern "C" fn ffi_wrapper<
-//! A, R,
-//! F: Fn(A) -> R,
-//! const f: F, // <-- this `const`-generic is not yet allowed
-//! >(arg: A) -> R {
-//! f(arg)
-//! }
-//! ```
-
-use std::mem;
-
-// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement.
-macro_rules! define_reify_functions {
- ($(
- fn $name:ident $(<$($param:ident),*>)?
- for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty;
- )+) => {
- $(pub const fn $name<
- $($($param,)*)?
- F: Fn($($arg_ty),*) -> $ret_ty + Copy
- >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty {
- // FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
- // formatting becomes possible in `const fn`.
- assert!(mem::size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
-
- $(extern $abi)? fn wrapper<
- $($($param,)*)?
- F: Fn($($arg_ty),*) -> $ret_ty + Copy
- >($($arg: $arg_ty),*) -> $ret_ty {
- let f = unsafe {
- // SAFETY: `F` satisfies all criteria for "out of thin air"
- // reconstructability (see module-level doc comment).
- mem::MaybeUninit::<F>::uninit().assume_init()
- };
- f($($arg),*)
- }
- let _f_proof = f;
- wrapper::<
- $($($param,)*)?
- F
- >
- })+
- }
-}
-
-define_reify_functions! {
- fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> R;
-
- // HACK(eddyb) this abstraction is used with `for<'a> fn(Bridge<'a>) -> T`
- // but that doesn't work with just `reify_to_extern_c_fn_unary` because of
- // the `fn` pointer type being "higher-ranked" (i.e. the `for<'a>` binder).
- // FIXME(eddyb) try to remove the lifetime from `Bridge`, that'd help.
- fn reify_to_extern_c_fn_hrt_bridge<R> for extern "C" fn(bridge: super::Bridge<'_>) -> R;
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
deleted file mode 100644
index 0fb3c6985..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
+++ /dev/null
@@ -1,332 +0,0 @@
-//! Server-side traits.
-
-use super::*;
-
-// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
-use super::client::HandleStore;
-
-pub trait Types {
- type FreeFunctions: 'static;
- type TokenStream: 'static + Clone;
- type Group: 'static + Clone;
- type Punct: 'static + Copy + Eq + Hash;
- type Ident: 'static + Copy + Eq + Hash;
- type Literal: 'static + Clone;
- type SourceFile: 'static + Clone;
- type MultiSpan: 'static;
- type Diagnostic: 'static;
- type Span: 'static + Copy + Eq + Hash;
-}
-
-/// Declare an associated fn of one of the traits below, adding necessary
-/// default bodies.
-macro_rules! associated_fn {
- (fn drop(&mut self, $arg:ident: $arg_ty:ty)) =>
- (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) });
-
- (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) =>
- (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() });
-
- ($($item:tt)*) => ($($item)*;)
-}
-
-macro_rules! declare_server_traits {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- $(pub trait $name: Types {
- $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
- })*
-
- pub trait Server: Types $(+ $name)* {}
- impl<S: Types $(+ $name)*> Server for S {}
- }
-}
-with_api!(Self, self_, declare_server_traits);
-
-pub(super) struct MarkedTypes<S: Types>(S);
-
-macro_rules! define_mark_types_impls {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- impl<S: Types> Types for MarkedTypes<S> {
- $(type $name = Marked<S::$name, client::$name>;)*
- }
-
- $(impl<S: $name> $name for MarkedTypes<S> {
- $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? {
- <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*))
- })*
- })*
- }
-}
-with_api!(Self, self_, define_mark_types_impls);
-
-struct Dispatcher<S: Types> {
- handle_store: HandleStore<S>,
- server: S,
-}
-
-macro_rules! define_dispatcher_impl {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- // FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
- pub trait DispatcherTrait {
- // HACK(eddyb) these are here to allow `Self::$name` to work below.
- $(type $name;)*
- fn dispatch(&mut self, buf: Buffer) -> Buffer;
- }
-
- impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> {
- $(type $name = <MarkedTypes<S> as Types>::$name;)*
- fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
- let Dispatcher { handle_store, server } = self;
-
- let mut reader = &buf[..];
- match api_tags::Method::decode(&mut reader, &mut ()) {
- $(api_tags::Method::$name(m) => match m {
- $(api_tags::$name::$method => {
- let mut call_method = || {
- reverse_decode!(reader, handle_store; $($arg: $arg_ty),*);
- $name::$method(server, $($arg),*)
- };
- // HACK(eddyb) don't use `panic::catch_unwind` in a panic.
- // If client and server happen to use the same `libstd`,
- // `catch_unwind` asserts that the panic counter was 0,
- // even when the closure passed to it didn't panic.
- let r = if thread::panicking() {
- Ok(call_method())
- } else {
- panic::catch_unwind(panic::AssertUnwindSafe(call_method))
- .map_err(PanicMessage::from)
- };
-
- buf.clear();
- r.encode(&mut buf, handle_store);
- })*
- }),*
- }
- buf
- }
- }
- }
-}
-with_api!(Self, self_, define_dispatcher_impl);
-
-pub trait ExecutionStrategy {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer;
-}
-
-pub struct SameThread;
-
-impl ExecutionStrategy for SameThread {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- let mut dispatch = |buf| dispatcher.dispatch(buf);
-
- run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- })
- }
-}
-
-// NOTE(eddyb) Two implementations are provided, the second one is a bit
-// faster but neither is anywhere near as fast as same-thread execution.
-
-pub struct CrossThread1;
-
-impl ExecutionStrategy for CrossThread1 {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- use std::sync::mpsc::channel;
-
- let (req_tx, req_rx) = channel();
- let (res_tx, res_rx) = channel();
-
- let join_handle = thread::spawn(move || {
- let mut dispatch = |buf| {
- req_tx.send(buf).unwrap();
- res_rx.recv().unwrap()
- };
-
- run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- })
- });
-
- for b in req_rx {
- res_tx.send(dispatcher.dispatch(b)).unwrap();
- }
-
- join_handle.join().unwrap()
- }
-}
-
-pub struct CrossThread2;
-
-impl ExecutionStrategy for CrossThread2 {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- use std::sync::{Arc, Mutex};
-
- enum State<T> {
- Req(T),
- Res(T),
- }
-
- let mut state = Arc::new(Mutex::new(State::Res(Buffer::new())));
-
- let server_thread = thread::current();
- let state2 = state.clone();
- let join_handle = thread::spawn(move || {
- let mut dispatch = |b| {
- *state2.lock().unwrap() = State::Req(b);
- server_thread.unpark();
- loop {
- thread::park();
- if let State::Res(b) = &mut *state2.lock().unwrap() {
- break b.take();
- }
- }
- };
-
- let r = run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- });
-
- // Wake up the server so it can exit the dispatch loop.
- drop(state2);
- server_thread.unpark();
-
- r
- });
-
- // Check whether `state2` was dropped, to know when to stop.
- while Arc::get_mut(&mut state).is_none() {
- thread::park();
- let mut b = match &mut *state.lock().unwrap() {
- State::Req(b) => b.take(),
- _ => continue,
- };
- b = dispatcher.dispatch(b.take());
- *state.lock().unwrap() = State::Res(b);
- join_handle.thread().unpark();
- }
-
- join_handle.join().unwrap()
- }
-}
-
-fn run_server<
- S: Server,
- I: Encode<HandleStore<MarkedTypes<S>>>,
- O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>,
->(
- strategy: &impl ExecutionStrategy,
- handle_counters: &'static client::HandleCounters,
- server: S,
- input: I,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
-) -> Result<O, PanicMessage> {
- let mut dispatcher =
- Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
-
- let mut buf = Buffer::new();
- input.encode(&mut buf, &mut dispatcher.handle_store);
-
- buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
-
- Result::decode(&mut &buf[..], &mut dispatcher.handle_store)
-}
-
-impl client::Client<super::super::TokenStream, super::super::TokenStream> {
- pub fn run<S>(
- &self,
- strategy: &impl ExecutionStrategy,
- server: S,
- input: S::TokenStream,
- force_show_panics: bool,
- ) -> Result<S::TokenStream, PanicMessage>
- where
- S: Server,
- S::TokenStream: Default,
- {
- let client::Client { get_handle_counters, run, _marker } = *self;
- run_server(
- strategy,
- get_handle_counters(),
- server,
- <MarkedTypes<S> as Types>::TokenStream::mark(input),
- run,
- force_show_panics,
- )
- .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
- }
-}
-
-impl
- client::Client<
- (super::super::TokenStream, super::super::TokenStream),
- super::super::TokenStream,
- >
-{
- pub fn run<S>(
- &self,
- strategy: &impl ExecutionStrategy,
- server: S,
- input: S::TokenStream,
- input2: S::TokenStream,
- force_show_panics: bool,
- ) -> Result<S::TokenStream, PanicMessage>
- where
- S: Server,
- S::TokenStream: Default,
- {
- let client::Client { get_handle_counters, run, _marker } = *self;
- run_server(
- strategy,
- get_handle_counters(),
- server,
- (
- <MarkedTypes<S> as Types>::TokenStream::mark(input),
- <MarkedTypes<S> as Types>::TokenStream::mark(input2),
- ),
- run,
- force_show_panics,
- )
- .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
deleted file mode 100644
index 3fade2dc4..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-//! lib-proc-macro diagnostic
-//!
-//! Copy from <https://github.com/rust-lang/rust/blob/e45d9973b2665897a768312e971b82cc62633103/src/libproc_macro/diagnostic.rs>
-//! augmented with removing unstable features
-
-use super::Span;
-
-/// An enum representing a diagnostic level.
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-pub enum Level {
- /// An error.
- Error,
- /// A warning.
- Warning,
- /// A note.
- Note,
- /// A help message.
- Help,
-}
-
-/// Trait implemented by types that can be converted into a set of `Span`s.
-pub trait MultiSpan {
- /// Converts `self` into a `Vec<Span>`.
- fn into_spans(self) -> Vec<Span>;
-}
-
-impl MultiSpan for Span {
- fn into_spans(self) -> Vec<Span> {
- vec![self]
- }
-}
-
-impl MultiSpan for Vec<Span> {
- fn into_spans(self) -> Vec<Span> {
- self
- }
-}
-
-impl<'a> MultiSpan for &'a [Span] {
- fn into_spans(self) -> Vec<Span> {
- self.to_vec()
- }
-}
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
- level: Level,
- message: String,
- spans: Vec<Span>,
- children: Vec<Diagnostic>,
-}
-
-macro_rules! diagnostic_child_methods {
- ($spanned:ident, $regular:ident, $level:expr) => {
- #[doc = concat!("Adds a new child diagnostics message to `self` with the [`",
- stringify!($level), "`] level, and the given `spans` and `message`.")]
- pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic
- where
- S: MultiSpan,
- T: Into<String>,
- {
- self.children.push(Diagnostic::spanned(spans, $level, message));
- self
- }
-
- #[doc = concat!("Adds a new child diagnostic message to `self` with the [`",
- stringify!($level), "`] level, and the given `message`.")]
- pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic {
- self.children.push(Diagnostic::new($level, message));
- self
- }
- };
-}
-
-/// Iterator over the children diagnostics of a `Diagnostic`.
-#[derive(Debug, Clone)]
-pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>);
-
-impl<'a> Iterator for Children<'a> {
- type Item = &'a Diagnostic;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next()
- }
-}
-
-impl Diagnostic {
- /// Creates a new diagnostic with the given `level` and `message`.
- pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
- Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
- }
-
- /// Creates a new diagnostic with the given `level` and `message` pointing to
- /// the given set of `spans`.
- pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic
- where
- S: MultiSpan,
- T: Into<String>,
- {
- Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] }
- }
-
- diagnostic_child_methods!(span_error, error, Level::Error);
- diagnostic_child_methods!(span_warning, warning, Level::Warning);
- diagnostic_child_methods!(span_note, note, Level::Note);
- diagnostic_child_methods!(span_help, help, Level::Help);
-
- /// Returns the diagnostic `level` for `self`.
- pub fn level(&self) -> Level {
- self.level
- }
-
- /// Sets the level in `self` to `level`.
- pub fn set_level(&mut self, level: Level) {
- self.level = level;
- }
-
- /// Returns the message in `self`.
- pub fn message(&self) -> &str {
- &self.message
- }
-
- /// Sets the message in `self` to `message`.
- pub fn set_message<T: Into<String>>(&mut self, message: T) {
- self.message = message.into();
- }
-
- /// Returns the `Span`s in `self`.
- pub fn spans(&self) -> &[Span] {
- &self.spans
- }
-
- /// Sets the `Span`s in `self` to `spans`.
- pub fn set_spans<S: MultiSpan>(&mut self, spans: S) {
- self.spans = spans.into_spans();
- }
-
- /// Returns an iterator over the children diagnostics of `self`.
- pub fn children(&self) -> Children<'_> {
- Children(self.children.iter())
- }
-
- /// Emit the diagnostic.
- pub fn emit(self) {
- fn to_internal(spans: Vec<Span>) -> super::bridge::client::MultiSpan {
- let mut multi_span = super::bridge::client::MultiSpan::new();
- for span in spans {
- multi_span.push(span.0);
- }
- multi_span
- }
-
- let mut diag = super::bridge::client::Diagnostic::new(
- self.level,
- &self.message[..],
- to_internal(self.spans),
- );
- for c in self.children {
- diag.sub(c.level, &c.message[..], to_internal(c.spans));
- }
- diag.emit();
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
deleted file mode 100644
index 89bd10da5..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
+++ /dev/null
@@ -1,1106 +0,0 @@
-//! A support library for macro authors when defining new macros.
-//!
-//! This library, provided by the standard distribution, provides the types
-//! consumed in the interfaces of procedurally defined macro definitions such as
-//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and
-//! custom derive attributes`#[proc_macro_derive]`.
-//!
-//! See [the book] for more.
-//!
-//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes
-
-#[doc(hidden)]
-pub mod bridge;
-
-mod diagnostic;
-
-pub use diagnostic::{Diagnostic, Level, MultiSpan};
-
-use std::cmp::Ordering;
-use std::ops::RangeBounds;
-use std::path::PathBuf;
-use std::str::FromStr;
-use std::{error, fmt, iter, mem};
-
-/// Determines whether proc_macro has been made accessible to the currently
-/// running program.
-///
-/// The proc_macro crate is only intended for use inside the implementation of
-/// procedural macros. All the functions in this crate panic if invoked from
-/// outside of a procedural macro, such as from a build script or unit test or
-/// ordinary Rust binary.
-///
-/// With consideration for Rust libraries that are designed to support both
-/// macro and non-macro use cases, `proc_macro::is_available()` provides a
-/// non-panicking way to detect whether the infrastructure required to use the
-/// API of proc_macro is presently available. Returns true if invoked from
-/// inside of a procedural macro, false if invoked from any other binary.
-pub fn is_available() -> bool {
- bridge::Bridge::is_available()
-}
-
-/// The main type provided by this crate, representing an abstract stream of
-/// tokens, or, more specifically, a sequence of token trees.
-/// The type provide interfaces for iterating over those token trees and, conversely,
-/// collecting a number of token trees into one stream.
-///
-/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
-/// and `#[proc_macro_derive]` definitions.
-#[derive(Clone)]
-pub struct TokenStream(Option<bridge::client::TokenStream>);
-
-/// Error returned from `TokenStream::from_str`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct LexError;
-
-impl fmt::Display for LexError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("cannot parse string into token stream")
- }
-}
-
-impl error::Error for LexError {}
-
-/// Error returned from `TokenStream::expand_expr`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct ExpandError;
-
-impl fmt::Display for ExpandError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("macro expansion failed")
- }
-}
-
-impl error::Error for ExpandError {}
-
-impl TokenStream {
- /// Returns an empty `TokenStream` containing no token trees.
- pub fn new() -> TokenStream {
- TokenStream(None)
- }
-
- /// Checks if this `TokenStream` is empty.
- pub fn is_empty(&self) -> bool {
- self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true)
- }
-
- /// Parses this `TokenStream` as an expression and attempts to expand any
- /// macros within it. Returns the expanded `TokenStream`.
- ///
- /// Currently only expressions expanding to literals will succeed, although
- /// this may be relaxed in the future.
- ///
- /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded,
- /// report an error, failing compilation, and/or return an `Err(..)`. The
- /// specific behavior for any error condition, and what conditions are
- /// considered errors, is unspecified and may change in the future.
- pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> {
- let stream = self.0.as_ref().ok_or(ExpandError)?;
- match bridge::client::TokenStream::expand_expr(stream) {
- Ok(stream) => Ok(TokenStream(Some(stream))),
- Err(_) => Err(ExpandError),
- }
- }
-}
-
-/// Attempts to break the string into tokens and parse those tokens into a token stream.
-/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
-/// or characters not existing in the language.
-/// All tokens in the parsed stream get `Span::call_site()` spans.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
-/// change these errors into `LexError`s later.
-impl FromStr for TokenStream {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<TokenStream, LexError> {
- Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src))))
- }
-}
-
-/// Prints the token stream as a string that is supposed to be losslessly convertible back
-/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenStream {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-/// Prints token in a form convenient for debugging.
-impl fmt::Debug for TokenStream {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("TokenStream ")?;
- f.debug_list().entries(self.clone()).finish()
- }
-}
-
-impl Default for TokenStream {
- fn default() -> Self {
- TokenStream::new()
- }
-}
-
-pub use quote::{quote, quote_span};
-
-fn tree_to_bridge_tree(
- tree: TokenTree,
-) -> bridge::TokenTree<
- bridge::client::Group,
- bridge::client::Punct,
- bridge::client::Ident,
- bridge::client::Literal,
-> {
- match tree {
- TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0),
- TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0),
- TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0),
- TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0),
- }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
- fn from(tree: TokenTree) -> TokenStream {
- TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree))))
- }
-}
-
-/// Non-generic helper for implementing `FromIterator<TokenStream>` and
-/// `Extend<TokenStream>` with less monomorphization in calling crates.
-struct ConcatStreamsHelper {
- streams: Vec<bridge::client::TokenStream>,
-}
-
-impl ConcatStreamsHelper {
- fn new(capacity: usize) -> Self {
- ConcatStreamsHelper { streams: Vec::with_capacity(capacity) }
- }
-
- fn push(&mut self, stream: TokenStream) {
- if let Some(stream) = stream.0 {
- self.streams.push(stream);
- }
- }
-
- fn build(mut self) -> TokenStream {
- if self.streams.len() <= 1 {
- TokenStream(self.streams.pop())
- } else {
- TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams)))
- }
- }
-
- fn append_to(mut self, stream: &mut TokenStream) {
- if self.streams.is_empty() {
- return;
- }
- let base = stream.0.take();
- if base.is_none() && self.streams.len() == 1 {
- stream.0 = self.streams.pop();
- } else {
- stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams));
- }
- }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
- trees.into_iter().map(TokenStream::from).collect()
- }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
- let iter = streams.into_iter();
- let mut builder = ConcatStreamsHelper::new(iter.size_hint().0);
- iter.for_each(|stream| builder.push(stream));
- builder.build()
- }
-}
-
-impl Extend<TokenTree> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
- self.extend(trees.into_iter().map(TokenStream::from));
- }
-}
-
-impl Extend<TokenStream> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
- // FIXME(eddyb) Use an optimized implementation if/when possible.
- *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect();
- }
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
- use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
-
- /// An iterator over `TokenStream`'s `TokenTree`s.
- /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
- /// and returns whole groups as token trees.
- #[derive(Clone)]
- pub struct IntoIter(
- std::vec::IntoIter<
- bridge::TokenTree<
- bridge::client::Group,
- bridge::client::Punct,
- bridge::client::Ident,
- bridge::client::Literal,
- >,
- >,
- );
-
- impl Iterator for IntoIter {
- type Item = TokenTree;
-
- fn next(&mut self) -> Option<TokenTree> {
- self.0.next().map(|tree| match tree {
- bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)),
- bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)),
- bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)),
- bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)),
- })
- }
- }
-
- impl IntoIterator for TokenStream {
- type Item = TokenTree;
- type IntoIter = IntoIter;
-
- fn into_iter(self) -> IntoIter {
- IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter())
- }
- }
-}
-
-#[doc(hidden)]
-mod quote;
-
-/// A region of source code, along with macro expansion information.
-#[derive(Copy, Clone)]
-pub struct Span(bridge::client::Span);
-
-macro_rules! diagnostic_method {
- ($name:ident, $level:expr) => {
- /// Creates a new `Diagnostic` with the given `message` at the span
- /// `self`.
- pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
- Diagnostic::spanned(self, $level, message)
- }
- };
-}
-
-impl Span {
- /// A span that resolves at the macro definition site.
- pub fn def_site() -> Span {
- Span(bridge::client::Span::def_site())
- }
-
- /// The span of the invocation of the current procedural macro.
- /// Identifiers created with this span will be resolved as if they were written
- /// directly at the macro call location (call-site hygiene) and other code
- /// at the macro call site will be able to refer to them as well.
- pub fn call_site() -> Span {
- Span(bridge::client::Span::call_site())
- }
-
- /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro
- /// definition site (local variables, labels, `$crate`) and sometimes at the macro
- /// call site (everything else).
- /// The span location is taken from the call-site.
- pub fn mixed_site() -> Span {
- Span(bridge::client::Span::mixed_site())
- }
-
- /// The original source file into which this span points.
- pub fn source_file(&self) -> SourceFile {
- SourceFile(self.0.source_file())
- }
-
- /// The `Span` for the tokens in the previous macro expansion from which
- /// `self` was generated from, if any.
- pub fn parent(&self) -> Option<Span> {
- self.0.parent().map(Span)
- }
-
- /// The span for the origin source code that `self` was generated from. If
- /// this `Span` wasn't generated from other macro expansions then the return
- /// value is the same as `*self`.
- pub fn source(&self) -> Span {
- Span(self.0.source())
- }
-
- /// Gets the starting line/column in the source file for this span.
- pub fn start(&self) -> LineColumn {
- self.0.start().add_1_to_column()
- }
-
- /// Gets the ending line/column in the source file for this span.
- pub fn end(&self) -> LineColumn {
- self.0.end().add_1_to_column()
- }
-
- /// Creates an empty span pointing to directly before this span.
- pub fn before(&self) -> Span {
- Span(self.0.before())
- }
-
- /// Creates an empty span pointing to directly after this span.
- pub fn after(&self) -> Span {
- Span(self.0.after())
- }
-
- /// Creates a new span encompassing `self` and `other`.
- ///
- /// Returns `None` if `self` and `other` are from different files.
- pub fn join(&self, other: Span) -> Option<Span> {
- self.0.join(other.0).map(Span)
- }
-
- /// Creates a new span with the same line/column information as `self` but
- /// that resolves symbols as though it were at `other`.
- pub fn resolved_at(&self, other: Span) -> Span {
- Span(self.0.resolved_at(other.0))
- }
-
- /// Creates a new span with the same name resolution behavior as `self` but
- /// with the line/column information of `other`.
- pub fn located_at(&self, other: Span) -> Span {
- other.resolved_at(*self)
- }
-
- /// Compares to spans to see if they're equal.
- pub fn eq(&self, other: &Span) -> bool {
- self.0 == other.0
- }
-
- /// Returns the source text behind a span. This preserves the original source
- /// code, including spaces and comments. It only returns a result if the span
- /// corresponds to real source code.
- ///
- /// Note: The observable result of a macro should only rely on the tokens and
- /// not on this source text. The result of this function is a best effort to
- /// be used for diagnostics only.
- pub fn source_text(&self) -> Option<String> {
- self.0.source_text()
- }
-
- // Used by the implementation of `Span::quote`
- #[doc(hidden)]
- pub fn save_span(&self) -> usize {
- self.0.save_span()
- }
-
- // Used by the implementation of `Span::quote`
- #[doc(hidden)]
- pub fn recover_proc_macro_span(id: usize) -> Span {
- Span(bridge::client::Span::recover_proc_macro_span(id))
- }
-
- diagnostic_method!(error, Level::Error);
- diagnostic_method!(warning, Level::Warning);
- diagnostic_method!(note, Level::Note);
- diagnostic_method!(help, Level::Help);
-}
-
-/// Prints a span in a form convenient for debugging.
-impl fmt::Debug for Span {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-/// A line-column pair representing the start or end of a `Span`.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct LineColumn {
- /// The 1-indexed line in the source file on which the span starts or ends (inclusive).
- pub line: usize,
- /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source
- /// file on which the span starts or ends (inclusive).
- pub column: usize,
-}
-
-impl LineColumn {
- fn add_1_to_column(self) -> Self {
- LineColumn { line: self.line, column: self.column + 1 }
- }
-}
-
-impl Ord for LineColumn {
- fn cmp(&self, other: &Self) -> Ordering {
- self.line.cmp(&other.line).then(self.column.cmp(&other.column))
- }
-}
-
-impl PartialOrd for LineColumn {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-/// The source file of a given `Span`.
-#[derive(Clone)]
-pub struct SourceFile(bridge::client::SourceFile);
-
-impl SourceFile {
- /// Gets the path to this source file.
- ///
- /// ### Note
- /// If the code span associated with this `SourceFile` was generated by an external macro, this
- /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check.
- ///
- /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on
- /// the command line, the path as given might not actually be valid.
- ///
- /// [`is_real`]: Self::is_real
- pub fn path(&self) -> PathBuf {
- PathBuf::from(self.0.path())
- }
-
- /// Returns `true` if this source file is a real source file, and not generated by an external
- /// macro's expansion.
- pub fn is_real(&self) -> bool {
- // This is a hack until intercrate spans are implemented and we can have real source files
- // for spans generated in external macros.
- // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
- self.0.is_real()
- }
-}
-
-impl fmt::Debug for SourceFile {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("SourceFile")
- .field("path", &self.path())
- .field("is_real", &self.is_real())
- .finish()
- }
-}
-
-impl PartialEq for SourceFile {
- fn eq(&self, other: &Self) -> bool {
- self.0.eq(&other.0)
- }
-}
-
-impl Eq for SourceFile {}
-
-/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`).
-#[derive(Clone)]
-pub enum TokenTree {
- /// A token stream surrounded by bracket delimiters.
- Group(Group),
- /// An identifier.
- Ident(Ident),
- /// A single punctuation character (`+`, `,`, `$`, etc.).
- Punct(Punct),
- /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc.
- Literal(Literal),
-}
-
-impl TokenTree {
- /// Returns the span of this tree, delegating to the `span` method of
- /// the contained token or a delimited stream.
- pub fn span(&self) -> Span {
- match *self {
- TokenTree::Group(ref t) => t.span(),
- TokenTree::Ident(ref t) => t.span(),
- TokenTree::Punct(ref t) => t.span(),
- TokenTree::Literal(ref t) => t.span(),
- }
- }
-
- /// Configures the span for *only this token*.
- ///
- /// Note that if this token is a `Group` then this method will not configure
- /// the span of each of the internal tokens, this will simply delegate to
- /// the `set_span` method of each variant.
- pub fn set_span(&mut self, span: Span) {
- match *self {
- TokenTree::Group(ref mut t) => t.set_span(span),
- TokenTree::Ident(ref mut t) => t.set_span(span),
- TokenTree::Punct(ref mut t) => t.set_span(span),
- TokenTree::Literal(ref mut t) => t.set_span(span),
- }
- }
-}
-
-/// Prints token tree in a form convenient for debugging.
-impl fmt::Debug for TokenTree {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Each of these has the name in the struct type in the derived debug,
- // so don't bother with an extra layer of indirection
- match *self {
- TokenTree::Group(ref tt) => tt.fmt(f),
- TokenTree::Ident(ref tt) => tt.fmt(f),
- TokenTree::Punct(ref tt) => tt.fmt(f),
- TokenTree::Literal(ref tt) => tt.fmt(f),
- }
- }
-}
-
-impl From<Group> for TokenTree {
- fn from(g: Group) -> TokenTree {
- TokenTree::Group(g)
- }
-}
-
-impl From<Ident> for TokenTree {
- fn from(g: Ident) -> TokenTree {
- TokenTree::Ident(g)
- }
-}
-
-impl From<Punct> for TokenTree {
- fn from(g: Punct) -> TokenTree {
- TokenTree::Punct(g)
- }
-}
-
-impl From<Literal> for TokenTree {
- fn from(g: Literal) -> TokenTree {
- TokenTree::Literal(g)
- }
-}
-
-/// Prints the token tree as a string that is supposed to be losslessly convertible back
-/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenTree {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-/// A delimited token stream.
-///
-/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s.
-#[derive(Clone)]
-pub struct Group(bridge::client::Group);
-
-/// Describes how a sequence of token trees is delimited.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Delimiter {
- /// `( ... )`
- Parenthesis,
- /// `{ ... }`
- Brace,
- /// `[ ... ]`
- Bracket,
- /// `Ø ... Ø`
- /// An invisible delimiter, that may, for example, appear around tokens coming from a
- /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
- /// `$var * 3` where `$var` is `1 + 2`.
- /// Invisible delimiters might not survive roundtrip of a token stream through a string.
- None,
-}
-
-impl Group {
- /// Creates a new `Group` with the given delimiter and token stream.
- ///
- /// This constructor will set the span for this group to
- /// `Span::call_site()`. To change the span you can use the `set_span`
- /// method below.
- pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group {
- Group(bridge::client::Group::new(delimiter, stream.0))
- }
-
- /// Returns the delimiter of this `Group`
- pub fn delimiter(&self) -> Delimiter {
- self.0.delimiter()
- }
-
- /// Returns the `TokenStream` of tokens that are delimited in this `Group`.
- ///
- /// Note that the returned token stream does not include the delimiter
- /// returned above.
- pub fn stream(&self) -> TokenStream {
- TokenStream(Some(self.0.stream()))
- }
-
- /// Returns the span for the delimiters of this token stream, spanning the
- /// entire `Group`.
- ///
- /// ```text
- /// pub fn span(&self) -> Span {
- /// ^^^^^^^
- /// ```
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Returns the span pointing to the opening delimiter of this group.
- ///
- /// ```text
- /// pub fn span_open(&self) -> Span {
- /// ^
- /// ```
- pub fn span_open(&self) -> Span {
- Span(self.0.span_open())
- }
-
- /// Returns the span pointing to the closing delimiter of this group.
- ///
- /// ```text
- /// pub fn span_close(&self) -> Span {
- /// ^
- /// ```
- pub fn span_close(&self) -> Span {
- Span(self.0.span_close())
- }
-
- /// Configures the span for this `Group`'s delimiters, but not its internal
- /// tokens.
- ///
- /// This method will **not** set the span of all the internal tokens spanned
- /// by this group, but rather it will only set the span of the delimiter
- /// tokens at the level of the `Group`.
- pub fn set_span(&mut self, span: Span) {
- self.0.set_span(span.0);
- }
-}
-
-/// Prints the group as a string that should be losslessly convertible back
-/// into the same group (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters.
-impl fmt::Display for Group {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Group {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Group")
- .field("delimiter", &self.delimiter())
- .field("stream", &self.stream())
- .field("span", &self.span())
- .finish()
- }
-}
-
-/// A `Punct` is a single punctuation character such as `+`, `-` or `#`.
-///
-/// Multi-character operators like `+=` are represented as two instances of `Punct` with different
-/// forms of `Spacing` returned.
-#[derive(Clone)]
-pub struct Punct(bridge::client::Punct);
-
-/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or
-/// by a different token or whitespace ([`Spacing::Alone`]).
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Spacing {
- /// A `Punct` is not immediately followed by another `Punct`.
- /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`.
- Alone,
- /// A `Punct` is immediately followed by another `Punct`.
- /// E.g. `+` is `Joint` in `+=` and `++`.
- ///
- /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`.
- Joint,
-}
-
-impl Punct {
- /// Creates a new `Punct` from the given character and spacing.
- /// The `ch` argument must be a valid punctuation character permitted by the language,
- /// otherwise the function will panic.
- ///
- /// The returned `Punct` will have the default span of `Span::call_site()`
- /// which can be further configured with the `set_span` method below.
- pub fn new(ch: char, spacing: Spacing) -> Punct {
- Punct(bridge::client::Punct::new(ch, spacing))
- }
-
- /// Returns the value of this punctuation character as `char`.
- pub fn as_char(&self) -> char {
- self.0.as_char()
- }
-
- /// Returns the spacing of this punctuation character, indicating whether it's immediately
- /// followed by another `Punct` in the token stream, so they can potentially be combined into
- /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace
- /// (`Alone`) so the operator has certainly ended.
- pub fn spacing(&self) -> Spacing {
- self.0.spacing()
- }
-
- /// Returns the span for this punctuation character.
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configure the span for this punctuation character.
- pub fn set_span(&mut self, span: Span) {
- self.0 = self.0.with_span(span.0);
- }
-}
-
-/// Prints the punctuation character as a string that should be losslessly convertible
-/// back into the same character.
-impl fmt::Display for Punct {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Punct {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Punct")
- .field("ch", &self.as_char())
- .field("spacing", &self.spacing())
- .field("span", &self.span())
- .finish()
- }
-}
-
-impl PartialEq<char> for Punct {
- fn eq(&self, rhs: &char) -> bool {
- self.as_char() == *rhs
- }
-}
-
-impl PartialEq<Punct> for char {
- fn eq(&self, rhs: &Punct) -> bool {
- *self == rhs.as_char()
- }
-}
-
-/// An identifier (`ident`).
-#[derive(Clone)]
-pub struct Ident(bridge::client::Ident);
-
-impl Ident {
- /// Creates a new `Ident` with the given `string` as well as the specified
- /// `span`.
- /// The `string` argument must be a valid identifier permitted by the
- /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic.
- ///
- /// Note that `span`, currently in rustc, configures the hygiene information
- /// for this identifier.
- ///
- /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene
- /// meaning that identifiers created with this span will be resolved as if they were written
- /// directly at the location of the macro call, and other code at the macro call site will be
- /// able to refer to them as well.
- ///
- /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene
- /// meaning that identifiers created with this span will be resolved at the location of the
- /// macro definition and other code at the macro call site will not be able to refer to them.
- ///
- /// Due to the current importance of hygiene this constructor, unlike other
- /// tokens, requires a `Span` to be specified at construction.
- pub fn new(string: &str, span: Span) -> Ident {
- Ident(bridge::client::Ident::new(string, span.0, false))
- }
-
- /// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
- /// The `string` argument be a valid identifier permitted by the language
- /// (including keywords, e.g. `fn`). Keywords which are usable in path segments
- /// (e.g. `self`, `super`) are not supported, and will cause a panic.
- pub fn new_raw(string: &str, span: Span) -> Ident {
- Ident(bridge::client::Ident::new(string, span.0, true))
- }
-
- /// Returns the span of this `Ident`, encompassing the entire string returned
- /// by [`to_string`](Self::to_string).
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configures the span of this `Ident`, possibly changing its hygiene context.
- pub fn set_span(&mut self, span: Span) {
- self.0 = self.0.with_span(span.0);
- }
-}
-
-/// Prints the identifier as a string that should be losslessly convertible
-/// back into the same identifier.
-impl fmt::Display for Ident {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Ident {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Ident")
- .field("ident", &self.to_string())
- .field("span", &self.span())
- .finish()
- }
-}
-
-/// A literal string (`"hello"`), byte string (`b"hello"`),
-/// character (`'a'`), byte character (`b'a'`), an integer or floating point number
-/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`).
-/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s.
-#[derive(Clone)]
-pub struct Literal(bridge::client::Literal);
-
-macro_rules! suffixed_int_literals {
- ($($name:ident => $kind:ident,)*) => ($(
- /// Creates a new suffixed integer literal with the specified value.
- ///
- /// This function will create an integer like `1u32` where the integer
- /// value specified is the first part of the token and the integral is
- /// also suffixed at the end.
- /// Literals created from negative numbers might not survive round-trips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// Literals created through this method have the `Span::call_site()`
- /// span by default, which can be configured with the `set_span` method
- /// below.
- pub fn $name(n: $kind) -> Literal {
- Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind)))
- }
- )*)
-}
-
-macro_rules! unsuffixed_int_literals {
- ($($name:ident => $kind:ident,)*) => ($(
- /// Creates a new unsuffixed integer literal with the specified value.
- ///
- /// This function will create an integer like `1` where the integer
- /// value specified is the first part of the token. No suffix is
- /// specified on this token, meaning that invocations like
- /// `Literal::i8_unsuffixed(1)` are equivalent to
- /// `Literal::u32_unsuffixed(1)`.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// Literals created through this method have the `Span::call_site()`
- /// span by default, which can be configured with the `set_span` method
- /// below.
- pub fn $name(n: $kind) -> Literal {
- Literal(bridge::client::Literal::integer(&n.to_string()))
- }
- )*)
-}
-
-impl Literal {
- suffixed_int_literals! {
- u8_suffixed => u8,
- u16_suffixed => u16,
- u32_suffixed => u32,
- u64_suffixed => u64,
- u128_suffixed => u128,
- usize_suffixed => usize,
- i8_suffixed => i8,
- i16_suffixed => i16,
- i32_suffixed => i32,
- i64_suffixed => i64,
- i128_suffixed => i128,
- isize_suffixed => isize,
- }
-
- unsuffixed_int_literals! {
- u8_unsuffixed => u8,
- u16_unsuffixed => u16,
- u32_unsuffixed => u32,
- u64_unsuffixed => u64,
- u128_unsuffixed => u128,
- usize_unsuffixed => usize,
- i8_unsuffixed => i8,
- i16_unsuffixed => i16,
- i32_unsuffixed => i32,
- i64_unsuffixed => i64,
- i128_unsuffixed => i128,
- isize_unsuffixed => isize,
- }
-
- /// Creates a new unsuffixed floating-point literal.
- ///
- /// This constructor is similar to those like `Literal::i8_unsuffixed` where
- /// the float's value is emitted directly into the token but no suffix is
- /// used, so it may be inferred to be a `f64` later in the compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f32_unsuffixed(n: f32) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- let mut repr = n.to_string();
- if !repr.contains('.') {
- repr.push_str(".0");
- }
- Literal(bridge::client::Literal::float(&repr))
- }
-
- /// Creates a new suffixed floating-point literal.
- ///
- /// This constructor will create a literal like `1.0f32` where the value
- /// specified is the preceding part of the token and `f32` is the suffix of
- /// the token. This token will always be inferred to be an `f32` in the
- /// compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f32_suffixed(n: f32) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- Literal(bridge::client::Literal::f32(&n.to_string()))
- }
-
- /// Creates a new unsuffixed floating-point literal.
- ///
- /// This constructor is similar to those like `Literal::i8_unsuffixed` where
- /// the float's value is emitted directly into the token but no suffix is
- /// used, so it may be inferred to be a `f64` later in the compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f64_unsuffixed(n: f64) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- let mut repr = n.to_string();
- if !repr.contains('.') {
- repr.push_str(".0");
- }
- Literal(bridge::client::Literal::float(&repr))
- }
-
- /// Creates a new suffixed floating-point literal.
- ///
- /// This constructor will create a literal like `1.0f64` where the value
- /// specified is the preceding part of the token and `f64` is the suffix of
- /// the token. This token will always be inferred to be an `f64` in the
- /// compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f64_suffixed(n: f64) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- Literal(bridge::client::Literal::f64(&n.to_string()))
- }
-
- /// String literal.
- pub fn string(string: &str) -> Literal {
- Literal(bridge::client::Literal::string(string))
- }
-
- /// Character literal.
- pub fn character(ch: char) -> Literal {
- Literal(bridge::client::Literal::character(ch))
- }
-
- /// Byte string literal.
- pub fn byte_string(bytes: &[u8]) -> Literal {
- Literal(bridge::client::Literal::byte_string(bytes))
- }
-
- /// Returns the span encompassing this literal.
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configures the span associated for this literal.
- pub fn set_span(&mut self, span: Span) {
- self.0.set_span(span.0);
- }
-
- /// Returns a `Span` that is a subset of `self.span()` containing only the
- /// source bytes in range `range`. Returns `None` if the would-be trimmed
- /// span is outside the bounds of `self`.
- // FIXME(SergioBenitez): check that the byte range starts and ends at a
- // UTF-8 boundary of the source. otherwise, it's likely that a panic will
- // occur elsewhere when the source text is printed.
- // FIXME(SergioBenitez): there is no way for the user to know what
- // `self.span()` actually maps to, so this method can currently only be
- // called blindly. For example, `to_string()` for the character 'c' returns
- // "'\u{63}'"; there is no way for the user to know whether the source text
- // was 'c' or whether it was '\u{63}'.
- pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
- self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span)
- }
-}
-
-/// Parse a single literal from its stringified representation.
-///
-/// In order to parse successfully, the input string must not contain anything
-/// but the literal token. Specifically, it must not contain whitespace or
-/// comments in addition to the literal.
-///
-/// The resulting literal token will have a `Span::call_site()` span.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We
-/// reserve the right to change these errors into `LexError`s later.
-impl FromStr for Literal {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<Self, LexError> {
- match bridge::client::Literal::from_str(src) {
- Ok(literal) => Ok(Literal(literal)),
- Err(()) => Err(LexError),
- }
- }
-}
-
-/// Prints the literal as a string that should be losslessly convertible
-/// back into the same literal (except for possible rounding for floating point literals).
-impl fmt::Display for Literal {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Literal {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-/// Tracked access to environment variables.
-pub mod tracked_env {
- use std::env::{self, VarError};
- use std::ffi::OsStr;
-
- /// Retrieve an environment variable and add it to build dependency info.
- /// Build system executing the compiler will know that the variable was accessed during
- /// compilation, and will be able to rerun the build when the value of that variable changes.
- /// Besides the dependency tracking this function should be equivalent to `env::var` from the
- /// standard library, except that the argument must be UTF-8.
- pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> {
- let key: &str = key.as_ref();
- let value = env::var(key);
- super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok());
- value
- }
-}
-
-/// Tracked access to additional files.
-pub mod tracked_path {
-
- /// Track a file explicitly.
- ///
- /// Commonly used for tracking asset preprocessing.
- pub fn path<P: AsRef<str>>(path: P) {
- let path: &str = path.as_ref();
- super::bridge::client::FreeFunctions::track_path(path);
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
deleted file mode 100644
index 39309faa4..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-//! # Quasiquoter
-//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
-
-//! This quasiquoter uses macros 2.0 hygiene to reliably access
-//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
-
-use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
-
-macro_rules! quote_tt {
- (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
- ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
- ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
- (,) => { Punct::new(',', Spacing::Alone) };
- (.) => { Punct::new('.', Spacing::Alone) };
- (;) => { Punct::new(';', Spacing::Alone) };
- (!) => { Punct::new('!', Spacing::Alone) };
- (<) => { Punct::new('<', Spacing::Alone) };
- (>) => { Punct::new('>', Spacing::Alone) };
- (&) => { Punct::new('&', Spacing::Alone) };
- (=) => { Punct::new('=', Spacing::Alone) };
- ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
-}
-
-macro_rules! quote_ts {
- ((@ $($t:tt)*)) => { $($t)* };
- (::) => {
- [
- TokenTree::from(Punct::new(':', Spacing::Joint)),
- TokenTree::from(Punct::new(':', Spacing::Alone)),
- ].iter()
- .cloned()
- .map(|mut x| {
- x.set_span(Span::def_site());
- x
- })
- .collect::<TokenStream>()
- };
- ($t:tt) => { TokenTree::from(quote_tt!($t)) };
-}
-
-/// Simpler version of the real `quote!` macro, implemented solely
-/// through `macro_rules`, for bootstrapping the real implementation
-/// (see the `quote` function), which does not have access to the
-/// real `quote!` macro due to the `proc_macro` crate not being
-/// able to depend on itself.
-///
-/// Note: supported tokens are a subset of the real `quote!`, but
-/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
-macro_rules! quote {
- () => { TokenStream::new() };
- ($($t:tt)*) => {
- [
- $(TokenStream::from(quote_ts!($t)),)*
- ].iter().cloned().collect::<TokenStream>()
- };
-}
-
-/// Quote a `TokenStream` into a `TokenStream`.
-/// This is the actual implementation of the `quote!()` proc macro.
-///
-/// It is loaded by the compiler in `register_builtin_macros`.
-pub fn quote(stream: TokenStream) -> TokenStream {
- if stream.is_empty() {
- return quote!(super::TokenStream::new());
- }
- let proc_macro_crate = quote!(crate);
- let mut after_dollar = false;
- let tokens = stream
- .into_iter()
- .filter_map(|tree| {
- if after_dollar {
- after_dollar = false;
- match tree {
- TokenTree::Ident(_) => {
- return Some(quote!(Into::<super::TokenStream>::into(
- Clone::clone(&(@ tree))),));
- }
- TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
- _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
- }
- } else if let TokenTree::Punct(ref tt) = tree {
- if tt.as_char() == '$' {
- after_dollar = true;
- return None;
- }
- }
-
- Some(quote!(super::TokenStream::from((@ match tree {
- TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new(
- (@ TokenTree::from(Literal::character(tt.as_char()))),
- (@ match tt.spacing() {
- Spacing::Alone => quote!(super::Spacing::Alone),
- Spacing::Joint => quote!(super::Spacing::Joint),
- }),
- ))),
- TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new(
- (@ match tt.delimiter() {
- Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis),
- Delimiter::Brace => quote!(super::Delimiter::Brace),
- Delimiter::Bracket => quote!(super::Delimiter::Bracket),
- Delimiter::None => quote!(super::Delimiter::None),
- }),
- (@ quote(tt.stream())),
- ))),
- TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new(
- (@ TokenTree::from(Literal::string(&tt.to_string()))),
- (@ quote_span(proc_macro_crate.clone(), tt.span())),
- ))),
- TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({
- let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
- .parse::<super::TokenStream>()
- .unwrap()
- .into_iter();
- if let (Some(super::TokenTree::Literal(mut lit)), None) =
- (iter.next(), iter.next())
- {
- lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
- lit
- } else {
- unreachable!()
- }
- }))
- })),))
- })
- .collect::<TokenStream>();
-
- if after_dollar {
- panic!("unexpected trailing `$` in `quote!`");
- }
-
- quote!([(@ tokens)].iter().cloned().collect::<super::TokenStream>())
-}
-
-/// Quote a `Span` into a `TokenStream`.
-/// This is needed to implement a custom quoter.
-pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
- let id = span.save_span();
- quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
deleted file mode 100644
index 30baf3a13..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
+++ /dev/null
@@ -1,840 +0,0 @@
-//! Rustc proc-macro server implementation with tt
-//!
-//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
-//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
-//! we could provide any TokenStream implementation.
-//! The original idea from fedochet is using proc-macro2 as backend,
-//! we use tt instead for better integration with RA.
-//!
-//! FIXME: No span and source file information is implemented yet
-
-use super::proc_macro::bridge::{self, server};
-
-use std::collections::HashMap;
-use std::hash::Hash;
-use std::ops::Bound;
-use std::{ascii, vec::IntoIter};
-
-use crate::tt;
-
-type Group = tt::Subtree;
-type TokenTree = tt::TokenTree;
-type Punct = tt::Punct;
-type Spacing = tt::Spacing;
-type Literal = tt::Literal;
-type Span = tt::TokenId;
-
-#[derive(Debug, Default, Clone)]
-pub struct TokenStream {
- pub token_trees: Vec<TokenTree>,
-}
-
-impl TokenStream {
- pub fn new() -> Self {
- TokenStream::default()
- }
-
- pub fn with_subtree(subtree: tt::Subtree) -> Self {
- if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
- TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
- } else {
- TokenStream { token_trees: subtree.token_trees }
- }
- }
-
- pub fn into_subtree(self) -> tt::Subtree {
- tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
- }
-
- pub fn is_empty(&self) -> bool {
- self.token_trees.is_empty()
- }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
- fn from(tree: TokenTree) -> TokenStream {
- TokenStream { token_trees: vec![tree] }
- }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
- trees.into_iter().map(TokenStream::from).collect()
- }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
- let mut builder = TokenStreamBuilder::new();
- streams.into_iter().for_each(|stream| builder.push(stream));
- builder.build()
- }
-}
-
-impl Extend<TokenTree> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
- self.extend(trees.into_iter().map(TokenStream::from));
- }
-}
-
-impl Extend<TokenStream> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
- for item in streams {
- for tkn in item {
- match tkn {
- tt::TokenTree::Subtree(subtree)
- if subtree.delimiter.kind == tt::DelimiterKind::Invisible =>
- {
- self.token_trees.extend(subtree.token_trees);
- }
- _ => {
- self.token_trees.push(tkn);
- }
- }
- }
- }
- }
-}
-
-#[derive(Clone)]
-pub struct SourceFile {
- // FIXME stub
-}
-
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
- level: Level,
- message: String,
- spans: Vec<Span>,
- children: Vec<Diagnostic>,
-}
-
-impl Diagnostic {
- /// Creates a new diagnostic with the given `level` and `message`.
- pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
- Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
- }
-}
-
-// Rustc Server Ident has to be `Copyable`
-// We use a stub here for bypassing
-#[derive(Hash, Eq, PartialEq, Copy, Clone)]
-pub struct IdentId(u32);
-
-#[derive(Clone, Hash, Eq, PartialEq)]
-struct IdentData(tt::Ident);
-
-#[derive(Default)]
-struct IdentInterner {
- idents: HashMap<IdentData, u32>,
- ident_data: Vec<IdentData>,
-}
-
-impl IdentInterner {
- fn intern(&mut self, data: &IdentData) -> u32 {
- if let Some(index) = self.idents.get(data) {
- return *index;
- }
-
- let index = self.idents.len() as u32;
- self.ident_data.push(data.clone());
- self.idents.insert(data.clone(), index);
- index
- }
-
- fn get(&self, index: u32) -> &IdentData {
- &self.ident_data[index as usize]
- }
-
- #[allow(unused)]
- fn get_mut(&mut self, index: u32) -> &mut IdentData {
- self.ident_data.get_mut(index as usize).expect("Should be consistent")
- }
-}
-
-pub struct TokenStreamBuilder {
- acc: TokenStream,
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
- use std::str::FromStr;
-
- use super::{tt, TokenStream, TokenTree};
-
- /// An iterator over `TokenStream`'s `TokenTree`s.
- /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
- /// and returns whole groups as token trees.
- impl IntoIterator for TokenStream {
- type Item = TokenTree;
- type IntoIter = super::IntoIter<TokenTree>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.token_trees.into_iter()
- }
- }
-
- type LexError = String;
-
- /// Attempts to break the string into tokens and parse those tokens into a token stream.
- /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
- /// or characters not existing in the language.
- /// All tokens in the parsed stream get `Span::call_site()` spans.
- ///
- /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
- /// change these errors into `LexError`s later.
- impl FromStr for TokenStream {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<TokenStream, LexError> {
- let (subtree, _token_map) =
- mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
-
- let subtree = subtree_replace_token_ids_with_unspecified(subtree);
- Ok(TokenStream::with_subtree(subtree))
- }
- }
-
- impl ToString for TokenStream {
- fn to_string(&self) -> String {
- ::tt::pretty(&self.token_trees)
- }
- }
-
- fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
- tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::UNSPECIFIED,
- close: tt::TokenId::UNSPECIFIED,
- ..subtree.delimiter
- },
- token_trees: subtree
- .token_trees
- .into_iter()
- .map(token_tree_replace_token_ids_with_unspecified)
- .collect(),
- }
- }
-
- fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
- match tt {
- tt::TokenTree::Leaf(leaf) => {
- tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
- }
- tt::TokenTree::Subtree(subtree) => {
- tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
- }
- }
- }
-
- fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
- match leaf {
- tt::Leaf::Literal(lit) => {
- tt::Leaf::Literal(tt::Literal { span: tt::TokenId::unspecified(), ..lit })
- }
- tt::Leaf::Punct(punct) => {
- tt::Leaf::Punct(tt::Punct { span: tt::TokenId::unspecified(), ..punct })
- }
- tt::Leaf::Ident(ident) => {
- tt::Leaf::Ident(tt::Ident { span: tt::TokenId::unspecified(), ..ident })
- }
- }
- }
-}
-
-impl TokenStreamBuilder {
- fn new() -> TokenStreamBuilder {
- TokenStreamBuilder { acc: TokenStream::new() }
- }
-
- fn push(&mut self, stream: TokenStream) {
- self.acc.extend(stream.into_iter())
- }
-
- fn build(self) -> TokenStream {
- self.acc
- }
-}
-
-pub struct FreeFunctions;
-
-#[derive(Clone)]
-pub struct TokenStreamIter {
- trees: IntoIter<TokenTree>,
-}
-
-#[derive(Default)]
-pub struct RustAnalyzer {
- ident_interner: IdentInterner,
- // FIXME: store span information here.
-}
-
-impl server::Types for RustAnalyzer {
- type FreeFunctions = FreeFunctions;
- type TokenStream = TokenStream;
- type Group = Group;
- type Punct = Punct;
- type Ident = IdentId;
- type Literal = Literal;
- type SourceFile = SourceFile;
- type Diagnostic = Diagnostic;
- type Span = Span;
- type MultiSpan = Vec<Span>;
-}
-
-impl server::FreeFunctions for RustAnalyzer {
- fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
- // FIXME: track env var accesses
- // https://github.com/rust-lang/rust/pull/71858
- }
- fn track_path(&mut self, _path: &str) {}
-}
-
-impl server::TokenStream for RustAnalyzer {
- fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
- stream.is_empty()
- }
- fn from_str(&mut self, src: &str) -> Self::TokenStream {
- use std::str::FromStr;
-
- Self::TokenStream::from_str(src).expect("cannot parse string")
- }
- fn to_string(&mut self, stream: &Self::TokenStream) -> String {
- stream.to_string()
- }
- fn from_token_tree(
- &mut self,
- tree: bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
- ) -> Self::TokenStream {
- match tree {
- bridge::TokenTree::Group(group) => {
- let tree = TokenTree::from(group);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Ident(IdentId(index)) => {
- let IdentData(ident) = self.ident_interner.get(index).clone();
- let ident: tt::Ident = ident;
- let leaf = tt::Leaf::from(ident);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Literal(literal) => {
- let leaf = tt::Leaf::from(literal);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Punct(p) => {
- let leaf = tt::Leaf::from(p);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
- }
- }
-
- fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
- Ok(self_.clone())
- }
-
- fn concat_trees(
- &mut self,
- base: Option<Self::TokenStream>,
- trees: Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>>,
- ) -> Self::TokenStream {
- let mut builder = TokenStreamBuilder::new();
- if let Some(base) = base {
- builder.push(base);
- }
- for tree in trees {
- builder.push(self.from_token_tree(tree));
- }
- builder.build()
- }
-
- fn concat_streams(
- &mut self,
- base: Option<Self::TokenStream>,
- streams: Vec<Self::TokenStream>,
- ) -> Self::TokenStream {
- let mut builder = TokenStreamBuilder::new();
- if let Some(base) = base {
- builder.push(base);
- }
- for stream in streams {
- builder.push(stream);
- }
- builder.build()
- }
-
- fn into_trees(
- &mut self,
- stream: Self::TokenStream,
- ) -> Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
- stream
- .into_iter()
- .map(|tree| match tree {
- tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
- bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident))))
- }
- tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit),
- tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct),
- tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(subtree),
- })
- .collect()
- }
-}
-
-fn delim_to_internal(d: bridge::Delimiter) -> tt::Delimiter {
- let kind = match d {
- bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
- bridge::Delimiter::Brace => tt::DelimiterKind::Brace,
- bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket,
- bridge::Delimiter::None => tt::DelimiterKind::Invisible,
- };
- tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
-}
-
-fn delim_to_external(d: tt::Delimiter) -> bridge::Delimiter {
- match d.kind {
- tt::DelimiterKind::Parenthesis => bridge::Delimiter::Parenthesis,
- tt::DelimiterKind::Brace => bridge::Delimiter::Brace,
- tt::DelimiterKind::Bracket => bridge::Delimiter::Bracket,
- tt::DelimiterKind::Invisible => bridge::Delimiter::None,
- }
-}
-
-fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing {
- match spacing {
- bridge::Spacing::Alone => Spacing::Alone,
- bridge::Spacing::Joint => Spacing::Joint,
- }
-}
-
-fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
- match spacing {
- Spacing::Alone => bridge::Spacing::Alone,
- Spacing::Joint => bridge::Spacing::Joint,
- }
-}
-
-impl server::Group for RustAnalyzer {
- fn new(
- &mut self,
- delimiter: bridge::Delimiter,
- stream: Option<Self::TokenStream>,
- ) -> Self::Group {
- Self::Group {
- delimiter: delim_to_internal(delimiter),
- token_trees: stream.unwrap_or_default().token_trees,
- }
- }
- fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
- delim_to_external(group.delimiter)
- }
-
- // NOTE: Return value of do not include delimiter
- fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
- TokenStream { token_trees: group.token_trees.clone() }
- }
-
- fn span(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.open
- }
-
- fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) {
- group.delimiter.open = span;
- }
-
- fn span_open(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.open
- }
-
- fn span_close(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.close
- }
-}
-
-impl server::Punct for RustAnalyzer {
- fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct {
- tt::Punct {
- char: ch,
- spacing: spacing_to_internal(spacing),
- span: tt::TokenId::unspecified(),
- }
- }
- fn as_char(&mut self, punct: Self::Punct) -> char {
- punct.char
- }
- fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing {
- spacing_to_external(punct.spacing)
- }
- fn span(&mut self, punct: Self::Punct) -> Self::Span {
- punct.span
- }
- fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct {
- tt::Punct { span: span, ..punct }
- }
-}
-
-impl server::Ident for RustAnalyzer {
- fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
- IdentId(self.ident_interner.intern(&IdentData(tt::Ident {
- text: if is_raw { ::tt::SmolStr::from_iter(["r#", string]) } else { string.into() },
- span,
- })))
- }
-
- fn span(&mut self, ident: Self::Ident) -> Self::Span {
- self.ident_interner.get(ident.0).0.span
- }
- fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident {
- let data = self.ident_interner.get(ident.0);
- let new = IdentData(tt::Ident { span: span, ..data.0.clone() });
- IdentId(self.ident_interner.intern(&new))
- }
-}
-
-impl server::Literal for RustAnalyzer {
- fn debug_kind(&mut self, _literal: &Self::Literal) -> String {
- // r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these.
- // They must still be present to be ABI-compatible and work with upstream proc_macro.
- "".to_owned()
- }
- fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
- Ok(Literal { text: s.into(), span: tt::TokenId::unspecified() })
- }
- fn symbol(&mut self, literal: &Self::Literal) -> String {
- literal.text.to_string()
- }
- fn suffix(&mut self, _literal: &Self::Literal) -> Option<String> {
- None
- }
-
- fn to_string(&mut self, literal: &Self::Literal) -> String {
- literal.to_string()
- }
-
- fn integer(&mut self, n: &str) -> Self::Literal {
- let n = match n.parse::<i128>() {
- Ok(n) => n.to_string(),
- Err(_) => n.parse::<u128>().unwrap().to_string(),
- };
- Literal { text: n.into(), span: tt::TokenId::unspecified() }
- }
-
- fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
- macro_rules! def_suffixed_integer {
- ($kind:ident, $($ty:ty),*) => {
- match $kind {
- $(
- stringify!($ty) => {
- let n: $ty = n.parse().unwrap();
- format!(concat!("{}", stringify!($ty)), n)
- }
- )*
- _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind),
- }
- }
- }
-
- let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize};
-
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn float(&mut self, n: &str) -> Self::Literal {
- let n: f64 = n.parse().unwrap();
- let mut text = f64::to_string(&n);
- if !text.contains('.') {
- text += ".0"
- }
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn f32(&mut self, n: &str) -> Self::Literal {
- let n: f32 = n.parse().unwrap();
- let text = format!("{n}f32");
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn f64(&mut self, n: &str) -> Self::Literal {
- let n: f64 = n.parse().unwrap();
- let text = format!("{n}f64");
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn string(&mut self, string: &str) -> Self::Literal {
- let mut escaped = String::new();
- for ch in string.chars() {
- escaped.extend(ch.escape_debug());
- }
- Literal { text: format!("\"{escaped}\"").into(), span: tt::TokenId::unspecified() }
- }
-
- fn character(&mut self, ch: char) -> Self::Literal {
- Literal { text: format!("'{ch}'").into(), span: tt::TokenId::unspecified() }
- }
-
- fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal {
- let string = bytes
- .iter()
- .cloned()
- .flat_map(ascii::escape_default)
- .map(Into::<char>::into)
- .collect::<String>();
-
- Literal { text: format!("b\"{string}\"").into(), span: tt::TokenId::unspecified() }
- }
-
- fn span(&mut self, literal: &Self::Literal) -> Self::Span {
- literal.span
- }
-
- fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) {
- literal.span = span;
- }
-
- fn subspan(
- &mut self,
- _literal: &Self::Literal,
- _start: Bound<usize>,
- _end: Bound<usize>,
- ) -> Option<Self::Span> {
- // FIXME handle span
- None
- }
-}
-
-impl server::SourceFile for RustAnalyzer {
- // FIXME these are all stubs
- fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
- true
- }
- fn path(&mut self, _file: &Self::SourceFile) -> String {
- String::new()
- }
- fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
- true
- }
-}
-
-impl server::Diagnostic for RustAnalyzer {
- fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
- let mut diag = Diagnostic::new(level, msg);
- diag.spans = spans;
- diag
- }
-
- fn sub(
- &mut self,
- _diag: &mut Self::Diagnostic,
- _level: Level,
- _msg: &str,
- _spans: Self::MultiSpan,
- ) {
- // FIXME handle diagnostic
- //
- }
-
- fn emit(&mut self, _diag: Self::Diagnostic) {
- // FIXME handle diagnostic
- // diag.emit()
- }
-}
-
-impl server::Span for RustAnalyzer {
- fn debug(&mut self, span: Self::Span) -> String {
- format!("{:?}", span.0)
- }
- fn def_site(&mut self) -> Self::Span {
- // MySpan(self.span_interner.intern(&MySpanData(Span::def_site())))
- // FIXME handle span
- tt::TokenId::unspecified()
- }
- fn call_site(&mut self) -> Self::Span {
- // MySpan(self.span_interner.intern(&MySpanData(Span::call_site())))
- // FIXME handle span
- tt::TokenId::unspecified()
- }
- fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
- SourceFile {}
- }
- fn save_span(&mut self, _span: Self::Span) -> usize {
- // FIXME stub
- 0
- }
- fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
- // FIXME stub
- tt::TokenId::unspecified()
- }
- /// Recent feature, not yet in the proc_macro
- ///
- /// See PR:
- /// https://github.com/rust-lang/rust/pull/55780
- fn source_text(&mut self, _span: Self::Span) -> Option<String> {
- None
- }
-
- fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
- // FIXME handle span
- None
- }
- fn source(&mut self, span: Self::Span) -> Self::Span {
- // FIXME handle span
- span
- }
- fn start(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn end(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
- // Just return the first span again, because some macros will unwrap the result.
- Some(first)
- }
- fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
- // FIXME handle span
- tt::TokenId::unspecified()
- }
-
- fn mixed_site(&mut self) -> Self::Span {
- // FIXME handle span
- tt::TokenId::unspecified()
- }
-
- fn after(&mut self, _self_: Self::Span) -> Self::Span {
- tt::TokenId::unspecified()
- }
-
- fn before(&mut self, _self_: Self::Span) -> Self::Span {
- tt::TokenId::unspecified()
- }
-}
-
-impl server::MultiSpan for RustAnalyzer {
- fn new(&mut self) -> Self::MultiSpan {
- // FIXME handle span
- vec![]
- }
-
- fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
- //TODP
- other.push(span)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::super::proc_macro::bridge::server::Literal;
- use super::*;
-
- #[test]
- fn test_ra_server_literals() {
- let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() };
- assert_eq!(srv.integer("1234").text, "1234");
-
- assert_eq!(srv.typed_integer("12", "u8").text, "12u8");
- assert_eq!(srv.typed_integer("255", "u16").text, "255u16");
- assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32");
- assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64");
- assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128");
- assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize");
- assert_eq!(srv.typed_integer("127", "i8").text, "127i8");
- assert_eq!(srv.typed_integer("255", "i16").text, "255i16");
- assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32");
- assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64");
- assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128");
- assert_eq!(srv.float("0").text, "0.0");
- assert_eq!(srv.float("15684.5867").text, "15684.5867");
- assert_eq!(srv.f32("15684.58").text, "15684.58f32");
- assert_eq!(srv.f64("15684.58").text, "15684.58f64");
-
- assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
- assert_eq!(srv.character('c').text, "'c'");
- assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
-
- // u128::max
- assert_eq!(
- srv.integer("340282366920938463463374607431768211455").text,
- "340282366920938463463374607431768211455"
- );
- // i128::min
- assert_eq!(
- srv.integer("-170141183460469231731687303715884105728").text,
- "-170141183460469231731687303715884105728"
- );
- }
-
- #[test]
- fn test_ra_server_to_string() {
- let s = TokenStream {
- token_trees: vec![
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "struct".into(),
- span: tt::TokenId::unspecified(),
- })),
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "T".into(),
- span: tt::TokenId::unspecified(),
- })),
- tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::unspecified(),
- close: tt::TokenId::unspecified(),
- kind: tt::DelimiterKind::Brace,
- },
- token_trees: vec![],
- }),
- ],
- };
-
- assert_eq!(s.to_string(), "struct T {}");
- }
-
- #[test]
- fn test_ra_server_from_str() {
- use std::str::FromStr;
- let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::unspecified(),
- close: tt::TokenId::unspecified(),
- kind: tt::DelimiterKind::Parenthesis,
- },
- token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "a".into(),
- span: tt::TokenId::unspecified(),
- }))],
- });
-
- let t1 = TokenStream::from_str("(a)").unwrap();
- assert_eq!(t1.token_trees.len(), 1);
- assert_eq!(t1.token_trees[0], subtree_paren_a);
-
- let t2 = TokenStream::from_str("(a);").unwrap();
- assert_eq!(t2.token_trees.len(), 2);
- assert_eq!(t2.token_trees[0], subtree_paren_a);
-
- let underscore = TokenStream::from_str("_").unwrap();
- assert_eq!(
- underscore.token_trees[0],
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "_".into(),
- span: tt::TokenId::unspecified(),
- }))
- );
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
deleted file mode 100644
index 04be39cff..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-//! Procedural macros are implemented by compiling the macro providing crate
-//! to a dynamic library with a particular ABI which the compiler uses to expand
-//! macros. Unfortunately this ABI is not specified and can change from version
-//! to version of the compiler. To support this we copy the ABI from the rust
-//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
-//!
-//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
-//! interface the rest of rust-analyzer can use to talk to the macro
-//! provider.
-//!
-//! # Adding a new ABI
-//!
-//! To add a new ABI you'll need to copy the source of the target proc_macro
-//! crate from the source tree of the Rust compiler into this directory tree.
-//! Then you'll need to modify it
-//! - Remove any feature! or other things which won't compile on stable
-//! - change any absolute imports to relative imports within the ABI tree
-//!
-//! Then you'll need to add a branch to the `Abi` enum and an implementation of
-//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
-//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
-//! need to update the conditionals in `Abi::from_lib` to return your new ABI
-//! for the relevant versions of the rust compiler
-//!
-
-mod abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-mod abi_sysroot;
-
-// see `build.rs`
-include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
-
-// Used by `test/utils.rs`
-#[cfg(all(test, feature = "sysroot-abi"))]
-pub(crate) use abi_sysroot::TokenStream as TestTokenStream;
-
-use super::dylib::LoadProcMacroDylibError;
-pub(crate) use abi_1_63::Abi as Abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-pub(crate) use abi_sysroot::Abi as Abi_Sysroot;
-use libloading::Library;
-use proc_macro_api::{ProcMacroKind, RustCInfo};
-
-use crate::tt;
-
-pub struct PanicMessage {
- message: Option<String>,
-}
-
-impl PanicMessage {
- pub fn as_str(&self) -> Option<String> {
- self.message.clone()
- }
-}
-
-pub(crate) enum Abi {
- Abi1_63(Abi_1_63),
- #[cfg(feature = "sysroot-abi")]
- AbiSysroot(Abi_Sysroot),
-}
-
-impl Abi {
- /// Load a new ABI.
- ///
- /// # Arguments
- ///
- /// *`lib` - The dynamic library containing the macro implementations
- /// *`symbol_name` - The symbol name the macros can be found attributes
- /// *`info` - RustCInfo about the compiler that was used to compile the
- /// macro crate. This is the information we use to figure out
- /// which ABI to return
- pub fn from_lib(
- lib: &Library,
- symbol_name: String,
- info: RustCInfo,
- ) -> Result<Abi, LoadProcMacroDylibError> {
- // the sysroot ABI relies on `extern proc_macro` with unstable features,
- // instead of a snapshot of the proc macro bridge's source code. it's only
- // enabled if we have an exact version match.
- #[cfg(feature = "sysroot-abi")]
- {
- if info.version_string == RUSTC_VERSION_STRING {
- let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
- return Ok(Abi::AbiSysroot(inner));
- }
-
- // if we reached this point, versions didn't match. in testing, we
- // want that to panic - this could mean that the format of `rustc
- // --version` no longer matches the format of the version string
- // stored in the `.rustc` section, and we want to catch that in-tree
- // with `x.py test`
- #[cfg(test)]
- {
- let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH");
- if let Ok("1") = allow_mismatch.as_deref() {
- // only used by rust-analyzer developers, when working on the
- // sysroot ABI from the rust-analyzer repository - which should
- // only happen pre-subtree. this can be removed later.
- } else {
- panic!(
- "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
- info.version_string, RUSTC_VERSION_STRING
- );
- }
- }
- }
-
- // FIXME: this should use exclusive ranges when they're stable
- // https://github.com/rust-lang/rust/issues/37854
- match (info.version.0, info.version.1) {
- (1, 63) => {
- let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
- Ok(Abi::Abi1_63(inner))
- }
- _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)),
- }
- }
-
- pub fn expand(
- &self,
- macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- match self {
- Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes),
- #[cfg(feature = "sysroot-abi")]
- Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes),
- }
- }
-
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- match self {
- Self::Abi1_63(abi) => abi.list_macros(),
- #[cfg(feature = "sysroot-abi")]
- Self::AbiSysroot(abi) => abi.list_macros(),
- }
- }
-}
-
-#[test]
-fn test_version_check() {
- let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
- let info = proc_macro_api::read_dylib_info(&path).unwrap();
- assert!(info.version.1 >= 50);
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs
deleted file mode 100644
index 05168feb6..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-//! Driver for proc macro server
-use std::io;
-
-use proc_macro_api::msg::{self, Message};
-
-use crate::ProcMacroSrv;
-
-pub fn run() -> io::Result<()> {
- let mut srv = ProcMacroSrv::default();
- let mut buf = String::new();
-
- while let Some(req) = read_request(&mut buf)? {
- let res = match req {
- msg::Request::ListMacros { dylib_path } => {
- msg::Response::ListMacros(srv.list_macros(&dylib_path))
- }
- msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
- msg::Request::ApiVersionCheck {} => {
- msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
- }
- };
- write_response(res)?
- }
-
- Ok(())
-}
-
-fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
- msg::Request::read(&mut io::stdin().lock(), buf)
-}
-
-fn write_response(msg: msg::Response) -> io::Result<()> {
- msg.write(&mut io::stdout().lock())
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index 89ffd1f49..dd05e250c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -13,10 +13,6 @@ use object::Object;
use paths::AbsPath;
use proc_macro_api::{read_dylib_info, ProcMacroKind};
-use crate::tt;
-
-use super::abis::Abi;
-
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
@@ -82,14 +78,17 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
pub enum LoadProcMacroDylibError {
Io(io::Error),
LibLoading(libloading::Error),
- UnsupportedABI(String),
+ AbiMismatch(String),
}
impl fmt::Display for LoadProcMacroDylibError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
- Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"),
+ Self::AbiMismatch(v) => {
+ use crate::RUSTC_VERSION_STRING;
+ write!(f, "mismatched ABI expected: `{RUSTC_VERSION_STRING}`, got `{v}`")
+ }
Self::LibLoading(e) => e.fmt(f),
}
}
@@ -110,7 +109,7 @@ impl From<libloading::Error> for LoadProcMacroDylibError {
struct ProcMacroLibraryLibloading {
// Hold on to the library so it doesn't unload
_lib: Library,
- abi: Abi,
+ proc_macros: crate::proc_macros::ProcMacros,
}
impl ProcMacroLibraryLibloading {
@@ -125,8 +124,9 @@ impl ProcMacroLibraryLibloading {
let version_info = read_dylib_info(abs_file)?;
let lib = load_library(file).map_err(invalid_data_err)?;
- let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
- Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
+ let proc_macros =
+ crate::proc_macros::ProcMacros::from_lib(&lib, symbol_name, version_info)?;
+ Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros })
}
}
@@ -150,15 +150,15 @@ impl Expander {
pub fn expand(
&self,
macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, String> {
- let result = self.inner.abi.expand(macro_name, macro_body, attributes);
+ macro_body: &crate::tt::Subtree,
+ attributes: Option<&crate::tt::Subtree>,
+ ) -> Result<crate::tt::Subtree, String> {
+ let result = self.inner.proc_macros.expand(macro_name, macro_body, attributes);
result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- self.inner.abi.list_macros()
+ self.inner.proc_macros.list_macros()
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index ee70fe7d4..84bd15efb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -10,17 +10,16 @@
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
+#![cfg(feature = "sysroot-abi")]
+#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
-#![cfg_attr(
- feature = "sysroot-abi",
- feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
-)]
#![allow(unreachable_pub)]
-mod dylib;
-mod abis;
+extern crate proc_macro;
-pub mod cli;
+mod dylib;
+mod server;
+mod proc_macros;
use std::{
collections::{hash_map::Entry, HashMap},
@@ -33,24 +32,27 @@ use std::{
};
use proc_macro_api::{
- msg::{ExpandMacro, FlatTree, PanicMessage},
+ msg::{self, CURRENT_API_VERSION},
ProcMacroKind,
};
use ::tt::token_id as tt;
+// see `build.rs`
+include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
+
#[derive(Default)]
-pub(crate) struct ProcMacroSrv {
+pub struct ProcMacroSrv {
expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
}
const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
impl ProcMacroSrv {
- pub fn expand(&mut self, task: ExpandMacro) -> Result<FlatTree, PanicMessage> {
+ pub fn expand(&mut self, task: msg::ExpandMacro) -> Result<msg::FlatTree, msg::PanicMessage> {
let expander = self.expander(task.lib.as_ref()).map_err(|err| {
debug_assert!(false, "should list macros before asking to expand");
- PanicMessage(format!("failed to load macro: {err}"))
+ msg::PanicMessage(format!("failed to load macro: {err}"))
})?;
let prev_env = EnvSnapshot::new();
@@ -68,8 +70,8 @@ impl ProcMacroSrv {
None => None,
};
- let macro_body = task.macro_body.to_subtree();
- let attributes = task.attributes.map(|it| it.to_subtree());
+ let macro_body = task.macro_body.to_subtree(CURRENT_API_VERSION);
+ let attributes = task.attributes.map(|it| it.to_subtree(CURRENT_API_VERSION));
let result = thread::scope(|s| {
let thread = thread::Builder::new()
.stack_size(EXPANDER_STACK_SIZE)
@@ -77,7 +79,7 @@ impl ProcMacroSrv {
.spawn_scoped(s, || {
expander
.expand(&task.macro_name, &macro_body, attributes.as_ref())
- .map(|it| FlatTree::new(&it))
+ .map(|it| msg::FlatTree::new(&it, CURRENT_API_VERSION))
});
let res = match thread {
Ok(handle) => handle.join(),
@@ -102,10 +104,10 @@ impl ProcMacroSrv {
}
}
- result.map_err(PanicMessage)
+ result.map_err(msg::PanicMessage)
}
- pub(crate) fn list_macros(
+ pub fn list_macros(
&mut self,
dylib_path: &Path,
) -> Result<Vec<(String, ProcMacroKind)>, String> {
@@ -129,6 +131,16 @@ impl ProcMacroSrv {
}
}
+pub struct PanicMessage {
+ message: Option<String>,
+}
+
+impl PanicMessage {
+ pub fn as_str(&self) -> Option<String> {
+ self.message.clone()
+ }
+}
+
struct EnvSnapshot {
vars: HashMap<OsString, OsString>,
}
@@ -138,10 +150,13 @@ impl EnvSnapshot {
EnvSnapshot { vars: env::vars_os().collect() }
}
- fn rollback(self) {
- let mut old_vars = self.vars;
+ fn rollback(self) {}
+}
+
+impl Drop for EnvSnapshot {
+ fn drop(&mut self) {
for (name, value) in env::vars_os() {
- let old_value = old_vars.remove(&name);
+ let old_value = self.vars.remove(&name);
if old_value != Some(value) {
match old_value {
None => env::remove_var(name),
@@ -149,13 +164,13 @@ impl EnvSnapshot {
}
}
}
- for (name, old_value) in old_vars {
+ for (name, old_value) in self.vars.drain() {
env::set_var(name, old_value)
}
}
}
-#[cfg(all(feature = "sysroot-abi", test))]
+#[cfg(test)]
mod tests;
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
index 0a3b8866a..3c6f32033 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,45 +1,55 @@
//! Proc macro ABI
-extern crate proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
use libloading::Library;
-use proc_macro_api::ProcMacroKind;
+use proc_macro_api::{ProcMacroKind, RustCInfo};
-use super::{tt, PanicMessage};
+use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt};
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
+pub(crate) struct ProcMacros {
exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
}
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
+impl From<proc_macro::bridge::PanicMessage> for crate::PanicMessage {
fn from(p: proc_macro::bridge::PanicMessage) -> Self {
Self { message: p.as_str().map(|s| s.to_string()) }
}
}
-impl Abi {
- pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
- let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
- lib.get(symbol_name.as_bytes())?;
- Ok(Self { exported_macros: macros.to_vec() })
+impl ProcMacros {
+ /// Load a new ABI.
+ ///
+ /// # Arguments
+ ///
+ /// *`lib` - The dynamic library containing the macro implementations
+ /// *`symbol_name` - The symbol name the macros can be found attributes
+ /// *`info` - RustCInfo about the compiler that was used to compile the
+ /// macro crate. This is the information we use to figure out
+ /// which ABI to return
+ pub(crate) fn from_lib(
+ lib: &Library,
+ symbol_name: String,
+ info: RustCInfo,
+ ) -> Result<ProcMacros, LoadProcMacroDylibError> {
+ if info.version_string == crate::RUSTC_VERSION_STRING {
+ let macros = unsafe {
+ lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes())
+ }?;
+
+ return Ok(Self { exported_macros: macros.to_vec() });
+ }
+ Err(LoadProcMacroDylibError::AbiMismatch(info.version_string))
}
- pub fn expand(
+ pub(crate) fn expand(
&self,
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- let parsed_body = ra_server::TokenStream::with_subtree(macro_body.clone());
+ ) -> Result<tt::Subtree, crate::PanicMessage> {
+ let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone());
- let parsed_attributes = attributes.map_or(ra_server::TokenStream::new(), |attr| {
- ra_server::TokenStream::with_subtree(attr.clone())
+ let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| {
+ crate::server::TokenStream::with_subtree(attr.clone())
});
for proc_macro in &self.exported_macros {
@@ -49,34 +59,34 @@ impl Abi {
} if *trait_name == macro_name => {
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_attributes,
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
_ => continue,
}
@@ -85,7 +95,7 @@ impl Abi {
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
}
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
+ pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
self.exported_macros
.iter()
.map(|proc_macro| match proc_macro {
@@ -102,3 +112,16 @@ impl Abi {
.collect()
}
}
+
+#[test]
+fn test_version_check() {
+ let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
+ let info = proc_macro_api::read_dylib_info(&path).unwrap();
+ assert_eq!(
+ info.version_string,
+ crate::RUSTC_VERSION_STRING,
+ "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
+ info.version_string,
+ crate::RUSTC_VERSION_STRING,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
index a9cd8e705..1980d4c78 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
@@ -8,10 +8,7 @@
//!
//! FIXME: No span and source file information is implemented yet
-use super::proc_macro::{
- self,
- bridge::{self, server},
-};
+use proc_macro::bridge::{self, server};
mod token_stream;
pub use token_stream::TokenStream;
@@ -26,8 +23,10 @@ use crate::tt;
type Group = tt::Subtree;
type TokenTree = tt::TokenTree;
+#[allow(unused)]
type Punct = tt::Punct;
type Spacing = tt::Spacing;
+#[allow(unused)]
type Literal = tt::Literal;
type Span = tt::TokenId;
@@ -36,14 +35,11 @@ pub struct SourceFile {
// FIXME stub
}
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
pub struct FreeFunctions;
-#[derive(Default)]
pub struct RustAnalyzer {
// FIXME: store span information here.
+ pub(crate) interner: SymbolInternerRef,
}
impl server::Types for RustAnalyzer {
@@ -68,7 +64,7 @@ impl server::FreeFunctions for RustAnalyzer {
// FIXME: keep track of LitKind and Suffix
Ok(bridge::Literal {
kind: bridge::LitKind::Err,
- symbol: Symbol::intern(s),
+ symbol: Symbol::intern(self.interner, s),
suffix: None,
span: tt::TokenId::unspecified(),
})
@@ -98,7 +94,7 @@ impl server::TokenStream for RustAnalyzer {
match tree {
bridge::TokenTree::Group(group) => {
let group = Group {
- delimiter: delim_to_internal(group.delimiter),
+ delimiter: delim_to_internal(group.delimiter, group.span),
token_trees: match group.stream {
Some(stream) => stream.into_iter().collect(),
None => Vec::new(),
@@ -109,7 +105,7 @@ impl server::TokenStream for RustAnalyzer {
}
bridge::TokenTree::Ident(ident) => {
- let text = ident.sym.text();
+ let text = ident.sym.text(self.interner);
let text =
if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text };
let ident: tt::Ident = tt::Ident { text, span: ident.span };
@@ -120,8 +116,9 @@ impl server::TokenStream for RustAnalyzer {
bridge::TokenTree::Literal(literal) => {
let literal = LiteralFormatter(literal);
- let text = literal
- .with_stringify_parts(|parts| ::tt::SmolStr::from_iter(parts.iter().copied()));
+ let text = literal.with_stringify_parts(self.interner, |parts| {
+ ::tt::SmolStr::from_iter(parts.iter().copied())
+ });
let literal = tt::Literal { text, span: literal.0.span };
let leaf = tt::Leaf::from(literal);
@@ -185,7 +182,7 @@ impl server::TokenStream for RustAnalyzer {
.map(|tree| match tree {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
bridge::TokenTree::Ident(bridge::Ident {
- sym: Symbol::intern(ident.text.trim_start_matches("r#")),
+ sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")),
is_raw: ident.text.starts_with("r#"),
span: ident.span,
})
@@ -194,7 +191,7 @@ impl server::TokenStream for RustAnalyzer {
bridge::TokenTree::Literal(bridge::Literal {
// FIXME: handle literal kinds
kind: bridge::LitKind::Err,
- symbol: Symbol::intern(&lit.text),
+ symbol: Symbol::intern(self.interner, &lit.text),
// FIXME: handle suffixes
suffix: None,
span: lit.span,
@@ -221,14 +218,14 @@ impl server::TokenStream for RustAnalyzer {
}
}
-fn delim_to_internal(d: proc_macro::Delimiter) -> tt::Delimiter {
+fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan<Span>) -> tt::Delimiter {
let kind = match d {
proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
proc_macro::Delimiter::Brace => tt::DelimiterKind::Brace,
proc_macro::Delimiter::Bracket => tt::DelimiterKind::Bracket,
proc_macro::Delimiter::None => tt::DelimiterKind::Invisible,
};
- tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
+ tt::Delimiter { open: span.open, close: span.close, kind }
}
fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
@@ -240,6 +237,7 @@ fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
}
}
+#[allow(unused)]
fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
match spacing {
proc_macro::Spacing::Alone => Spacing::Alone,
@@ -247,6 +245,7 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
}
}
+#[allow(unused)]
fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
match spacing {
Spacing::Alone => proc_macro::Spacing::Alone,
@@ -302,14 +301,6 @@ impl server::Span for RustAnalyzer {
// FIXME handle span
Range { start: 0, end: 0 }
}
- fn start(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn end(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
// Just return the first span again, because some macros will unwrap the result.
Some(first)
@@ -328,13 +319,23 @@ impl server::Span for RustAnalyzer {
tt::TokenId::unspecified()
}
- fn after(&mut self, _self_: Self::Span) -> Self::Span {
+ fn end(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
- fn before(&mut self, _self_: Self::Span) -> Self::Span {
+ fn start(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
+
+ fn line(&mut self, _span: Self::Span) -> usize {
+ // FIXME handle line
+ 0
+ }
+
+ fn column(&mut self, _span: Self::Span) -> usize {
+ // FIXME handle column
+ 0
+ }
}
impl server::Symbol for RustAnalyzer {
@@ -354,11 +355,13 @@ impl server::Server for RustAnalyzer {
}
fn intern_symbol(ident: &str) -> Self::Symbol {
- Symbol::intern(&::tt::SmolStr::from(ident))
+ // FIXME: should be self.interner once the proc-macro api allows is
+ Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident))
}
fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
- f(symbol.text().as_str())
+ // FIXME: should be self.interner once the proc-macro api allows is
+ f(symbol.text(&SYMBOL_INTERNER).as_str())
}
}
@@ -369,7 +372,11 @@ impl LiteralFormatter {
/// literal's representation. This is done to allow the `ToString` and
/// `Display` implementations to borrow references to symbol values, and
/// both be optimized to reduce overhead.
- fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R {
+ fn with_stringify_parts<R>(
+ &self,
+ interner: SymbolInternerRef,
+ f: impl FnOnce(&[&str]) -> R,
+ ) -> R {
/// Returns a string containing exactly `num` '#' characters.
/// Uses a 256-character source string literal which is always safe to
/// index with a `u8` index.
@@ -384,7 +391,7 @@ impl LiteralFormatter {
&HASHES[..num as usize]
}
- self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind {
+ self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind {
bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
@@ -401,9 +408,13 @@ impl LiteralFormatter {
})
}
- fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R {
- let symbol = self.0.symbol.text();
- let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default();
+ fn with_symbol_and_suffix<R>(
+ &self,
+ interner: SymbolInternerRef,
+ f: impl FnOnce(&str, &str) -> R,
+ ) -> R {
+ let symbol = self.0.symbol.text(interner);
+ let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default();
f(symbol.as_str(), suffix.as_str())
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs
index 51dfba2ea..540d06457 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs
@@ -1,28 +1,30 @@
//! Symbol interner for proc-macro-srv
-use std::{cell::RefCell, collections::HashMap};
+use std::{cell::RefCell, collections::HashMap, thread::LocalKey};
use tt::SmolStr;
thread_local! {
- static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
+ pub(crate) static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
}
// ID for an interned symbol.
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
pub struct Symbol(u32);
+pub(crate) type SymbolInternerRef = &'static LocalKey<RefCell<SymbolInterner>>;
+
impl Symbol {
- pub fn intern(data: &str) -> Symbol {
- SYMBOL_INTERNER.with(|i| i.borrow_mut().intern(data))
+ pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol {
+ interner.with(|i| i.borrow_mut().intern(data))
}
- pub fn text(&self) -> SmolStr {
- SYMBOL_INTERNER.with(|i| i.borrow().get(self).clone())
+ pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr {
+ interner.with(|i| i.borrow().get(self).clone())
}
}
#[derive(Default)]
-struct SymbolInterner {
+pub(crate) struct SymbolInterner {
idents: HashMap<SmolStr, u32>,
ident_data: Vec<SmolStr>,
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs
index d091d4319..2589d8b64 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs
@@ -4,15 +4,15 @@ use crate::tt::{self, TokenTree};
#[derive(Debug, Default, Clone)]
pub struct TokenStream {
- pub token_trees: Vec<TokenTree>,
+ pub(super) token_trees: Vec<TokenTree>,
}
impl TokenStream {
- pub fn new() -> Self {
+ pub(crate) fn new() -> Self {
TokenStream::default()
}
- pub fn with_subtree(subtree: tt::Subtree) -> Self {
+ pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self {
if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
} else {
@@ -20,11 +20,11 @@ impl TokenStream {
}
}
- pub fn into_subtree(self) -> tt::Subtree {
+ pub(crate) fn into_subtree(self) -> tt::Subtree {
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
}
- pub fn is_empty(&self) -> bool {
+ pub(super) fn is_empty(&self) -> bool {
self.token_trees.is_empty()
}
}
@@ -78,12 +78,12 @@ impl Extend<TokenStream> for TokenStream {
}
}
-pub struct TokenStreamBuilder {
+pub(super) struct TokenStreamBuilder {
acc: TokenStream,
}
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
+/// pub(super)lic implementation details for the `TokenStream` type, such as iterators.
+pub(super) mod token_stream {
use std::str::FromStr;
use super::{tt, TokenStream, TokenTree};
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index efbeb90ca..49b4d973b 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -5,14 +5,14 @@ use std::str::FromStr;
use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv};
-fn parse_string(code: &str) -> Option<crate::abis::TestTokenStream> {
+fn parse_string(code: &str) -> Option<crate::server::TokenStream> {
// This is a bit strange. We need to parse a string into a token stream into
// order to create a tt::SubTree from it in fixtures. `into_subtree` is
// implemented by all the ABIs we have so we arbitrarily choose one ABI to
// write a `parse_string` function for and use that. The tests don't really
// care which ABI we're using as the `into_subtree` function isn't part of
// the ABI and shouldn't change between ABI versions.
- crate::abis::TestTokenStream::from_str(code).ok()
+ crate::server::TokenStream::from_str(code).ok()
}
pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 6273ea51d..602e74275 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -20,7 +20,7 @@ countme = { version = "3.0.1", features = ["enable"] }
jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
-perf-event = "0.4.7"
+perf-event = "=0.4.7"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] }
diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
index 8017f8657..f089c78e0 100644
--- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
+++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
@@ -91,6 +91,12 @@ fn memusage_linux() -> MemoryUsage {
pub struct Bytes(isize);
impl Bytes {
+ pub fn new(bytes: isize) -> Bytes {
+ Bytes(bytes)
+ }
+}
+
+impl Bytes {
pub fn megabytes(self) -> isize {
self.0 / 1024 / 1024
}
diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
index 22d6a6e78..3abff64a8 100644
--- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
@@ -16,10 +16,12 @@ tracing = "0.1.35"
rustc-hash = "1.1.0"
cargo_metadata = "0.15.0"
semver = "1.0.14"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.86"
+serde_json.workspace = true
+serde.workspace = true
+triomphe.workspace = true
anyhow = "1.0.62"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
+itertools = "0.10.5"
# local deps
base-db.workspace = true
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index 4e5d640f1..6cbf403cb 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -14,9 +14,10 @@ use std::{
};
use cargo_metadata::{camino::Utf8Path, Message};
+use itertools::Itertools;
use la_arena::ArenaMap;
use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use serde::Deserialize;
@@ -56,7 +57,10 @@ impl BuildScriptOutput {
}
impl WorkspaceBuildScripts {
- fn build_command(config: &CargoConfig) -> io::Result<Command> {
+ fn build_command(
+ config: &CargoConfig,
+ allowed_features: &FxHashSet<String>,
+ ) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
Some([program, args @ ..]) => {
let mut cmd = Command::new(program);
@@ -88,7 +92,12 @@ impl WorkspaceBuildScripts {
}
if !features.is_empty() {
cmd.arg("--features");
- cmd.arg(features.join(" "));
+ cmd.arg(
+ features
+ .iter()
+ .filter(|&feat| allowed_features.contains(feat))
+ .join(","),
+ );
}
}
}
@@ -127,13 +136,20 @@ impl WorkspaceBuildScripts {
}
.as_ref();
- match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) {
+ let allowed_features = workspace.workspace_features();
+
+ match Self::run_per_ws(
+ Self::build_command(config, &allowed_features)?,
+ workspace,
+ current_dir,
+ progress,
+ ) {
Ok(WorkspaceBuildScripts { error: Some(error), .. })
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
{
// building build scripts failed, attempt to build with --keep-going so
// that we potentially get more build data
- let mut cmd = Self::build_command(config)?;
+ let mut cmd = Self::build_command(config, &allowed_features)?;
cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
res.error = Some(error);
@@ -161,7 +177,7 @@ impl WorkspaceBuildScripts {
))
}
};
- let cmd = Self::build_command(config)?;
+ let cmd = Self::build_command(config, &Default::default())?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
@@ -415,7 +431,6 @@ impl WorkspaceBuildScripts {
let dir_entry = entry.ok()?;
if dir_entry.file_type().ok()?.is_file() {
let path = dir_entry.path();
- tracing::info!("p{:?}", path);
let extension = path.extension()?;
if extension == std::env::consts::DLL_EXTENSION {
let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned();
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 01162b1a8..92b454150 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -1,6 +1,5 @@
//! See [`CargoWorkspace`].
-use std::iter;
use std::path::PathBuf;
use std::str::from_utf8;
use std::{ops, process::Command};
@@ -10,7 +9,7 @@ use base_db::Edition;
use cargo_metadata::{CargoOpt, MetadataCommand};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
use serde_json::from_value;
@@ -32,6 +31,7 @@ pub struct CargoWorkspace {
packages: Arena<PackageData>,
targets: Arena<TargetData>,
workspace_root: AbsPathBuf,
+ target_directory: AbsPathBuf,
}
impl ops::Index<Package> for CargoWorkspace {
@@ -57,20 +57,6 @@ pub enum RustLibSource {
Discover,
}
-/// Crates to disable `#[cfg(test)]` on.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum UnsetTestCrates {
- None,
- Only(Vec<String>),
- All,
-}
-
-impl Default for UnsetTestCrates {
- fn default() -> Self {
- Self::None
- }
-}
-
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CargoFeatures {
All,
@@ -99,8 +85,7 @@ pub struct CargoConfig {
pub sysroot_src: Option<AbsPathBuf>,
/// rustc private crate source
pub rustc_source: Option<RustLibSource>,
- /// crates to disable `#[cfg(test)]` on
- pub unset_test_crates: UnsetTestCrates,
+ pub cfg_overrides: CfgOverrides,
/// Invoke `cargo check` through the RUSTC_WRAPPER.
pub wrap_rustc_in_build_scripts: bool,
/// The command to run instead of `cargo check` for building build scripts.
@@ -113,27 +98,6 @@ pub struct CargoConfig {
pub invocation_location: InvocationLocation,
}
-impl CargoConfig {
- pub fn cfg_overrides(&self) -> CfgOverrides {
- match &self.unset_test_crates {
- UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()),
- UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
- unset_test_crates
- .iter()
- .cloned()
- .zip(iter::repeat_with(|| {
- cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
- .unwrap()
- }))
- .collect(),
- ),
- UnsetTestCrates::All => CfgOverrides::Wildcard(
- cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(),
- ),
- }
- }
-}
-
pub type Package = Idx<PackageData>;
pub type Target = Idx<TargetData>;
@@ -293,13 +257,29 @@ impl CargoWorkspace {
}
meta.current_dir(current_dir.as_os_str());
+ let mut other_options = vec![];
+ // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
+ // the only relevant flags for metadata here are unstable ones, so we pass those along
+ // but nothing else
+ let mut extra_args = config.extra_args.iter();
+ while let Some(arg) = extra_args.next() {
+ if arg == "-Z" {
+ if let Some(arg) = extra_args.next() {
+ other_options.push("-Z".to_owned());
+ other_options.push(arg.to_owned());
+ }
+ }
+ }
+
if !targets.is_empty() {
- let other_options: Vec<_> = targets
- .into_iter()
- .flat_map(|target| ["--filter-platform".to_string(), target])
- .collect();
- meta.other_options(other_options);
+ other_options.append(
+ &mut targets
+ .into_iter()
+ .flat_map(|target| ["--filter-platform".to_owned().to_string(), target])
+ .collect(),
+ );
}
+ meta.other_options(other_options);
// FIXME: Fetching metadata is a slow process, as it might require
// calling crates.io. We should be reporting progress here, but it's
@@ -411,7 +391,10 @@ impl CargoWorkspace {
let workspace_root =
AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string()));
- CargoWorkspace { packages, targets, workspace_root }
+ let target_directory =
+ AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string()));
+
+ CargoWorkspace { packages, targets, workspace_root, target_directory }
}
pub fn packages(&self) -> impl Iterator<Item = Package> + ExactSizeIterator + '_ {
@@ -429,6 +412,10 @@ impl CargoWorkspace {
&self.workspace_root
}
+ pub fn target_directory(&self) -> &AbsPath {
+ &self.target_directory
+ }
+
pub fn package_flag(&self, package: &PackageData) -> String {
if self.is_unique(&package.name) {
package.name.clone()
@@ -467,6 +454,21 @@ impl CargoWorkspace {
None
}
+ /// Returns the union of the features of all member crates in this workspace.
+ pub fn workspace_features(&self) -> FxHashSet<String> {
+ self.packages()
+ .filter_map(|package| {
+ let package = &self[package];
+ if package.is_member {
+ Some(package.features.keys().cloned())
+ } else {
+ None
+ }
+ })
+ .flatten()
+ .collect()
+ }
+
fn is_unique(&self, name: &str) -> bool {
self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
index c134b78ab..e366d441c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
@@ -49,6 +49,14 @@ impl Extend<CfgFlag> for CfgOptions {
}
}
+impl FromIterator<CfgFlag> for CfgOptions {
+ fn from_iter<T: IntoIterator<Item = CfgFlag>>(iter: T) -> Self {
+ let mut this = CfgOptions::default();
+ this.extend(iter);
+ this
+ }
+}
+
impl fmt::Display for CfgFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 70cb71ae3..61acc646f 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -44,7 +44,7 @@ pub use crate::{
build_scripts::WorkspaceBuildScripts,
cargo_workspace::{
CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
- RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates,
+ RustLibSource, Target, TargetData, TargetKind,
},
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
index 980d92d3d..3f60e4dd9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
@@ -34,6 +34,10 @@ impl ManifestPath {
pub fn parent(&self) -> &AbsPath {
self.file.parent().unwrap()
}
+
+ pub fn canonicalize(&self) -> ! {
+ (&**self).canonicalize()
+ }
}
impl ops::Deref for ManifestPath {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 4b2448e47..80897f747 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -49,12 +49,12 @@
//! user explores them belongs to that extension (it's totally valid to change
//! rust-project.json over time via configuration request!)
-use std::path::PathBuf;
-
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
+use la_arena::RawIdx;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::{de, Deserialize};
+use std::path::PathBuf;
use crate::cfg_flag::CfgFlag;
@@ -98,26 +98,23 @@ impl ProjectJson {
/// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via
/// configuration.
pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
+ let absolutize_on_base = |p| base.absolutize(p);
ProjectJson {
- sysroot: data.sysroot.map(|it| base.join(it)),
- sysroot_src: data.sysroot_src.map(|it| base.join(it)),
+ sysroot: data.sysroot.map(absolutize_on_base),
+ sysroot_src: data.sysroot_src.map(absolutize_on_base),
project_root: base.to_path_buf(),
crates: data
.crates
.into_iter()
.map(|crate_data| {
- let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| {
- crate_data.root_module.is_relative()
- && !crate_data.root_module.starts_with("..")
- || crate_data.root_module.starts_with(base)
- });
- let root_module = base.join(crate_data.root_module).normalize();
+ let root_module = absolutize_on_base(crate_data.root_module);
+ let is_workspace_member = crate_data
+ .is_workspace_member
+ .unwrap_or_else(|| root_module.starts_with(base));
let (include, exclude) = match crate_data.source {
Some(src) => {
let absolutize = |dirs: Vec<PathBuf>| {
- dirs.into_iter()
- .map(|it| base.join(it).normalize())
- .collect::<Vec<_>>()
+ dirs.into_iter().map(absolutize_on_base).collect::<Vec<_>>()
};
(absolutize(src.include_dirs), absolutize(src.exclude_dirs))
}
@@ -135,7 +132,10 @@ impl ProjectJson {
.deps
.into_iter()
.map(|dep_data| {
- Dependency::new(dep_data.name, CrateId(dep_data.krate as u32))
+ Dependency::new(
+ dep_data.name,
+ CrateId::from_raw(RawIdx::from(dep_data.krate as u32)),
+ )
})
.collect::<Vec<_>>(),
cfg: crate_data.cfg,
@@ -143,7 +143,7 @@ impl ProjectJson {
env: crate_data.env,
proc_macro_dylib_path: crate_data
.proc_macro_dylib_path
- .map(|it| base.join(it)),
+ .map(absolutize_on_base),
is_workspace_member,
include,
exclude,
@@ -151,7 +151,7 @@ impl ProjectJson {
repository: crate_data.repository,
}
})
- .collect::<Vec<_>>(),
+ .collect(),
}
}
@@ -162,7 +162,10 @@ impl ProjectJson {
/// Returns an iterator over the crates in the project.
pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
- self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate))
+ self.crates
+ .iter()
+ .enumerate()
+ .map(|(idx, krate)| (CrateId::from_raw(RawIdx::from(idx as u32)), krate))
}
/// Returns the path to the project's root folder.
@@ -236,7 +239,7 @@ struct CrateSource {
exclude_dirs: Vec<PathBuf>,
}
-fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
+fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error>
where
D: de::Deserializer<'de>,
{
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 74e41eda7..e3a2de927 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -12,13 +12,15 @@ use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
-use crate::{utf8_stdout, ManifestPath};
+use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
root: AbsPathBuf,
src_root: AbsPathBuf,
crates: Arena<SysrootCrateData>,
+ /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace.
+ pub hack_cargo_workspace: Option<CargoWorkspace>,
}
pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
@@ -74,6 +76,23 @@ impl Sysroot {
pub fn is_empty(&self) -> bool {
self.crates.is_empty()
}
+
+ pub fn loading_warning(&self) -> Option<String> {
+ if self.by_name("core").is_none() {
+ let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
+ " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
+ } else {
+ " try running `rustup component add rust-src` to possible fix this"
+ };
+ Some(format!(
+ "could not find libcore in loaded sysroot at `{}`{}",
+ self.src_root.as_path().display(),
+ var_note,
+ ))
+ } else {
+ None
+ }
+ }
}
// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
@@ -103,14 +122,36 @@ impl Sysroot {
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
- format_err!("can't load standard library from sysroot {}", sysroot_dir.display())
+ format_err!("can't load standard library from sysroot path {}", sysroot_dir.display())
})?;
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
}
- pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot {
- let mut sysroot =
- Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };
+ pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot {
+ // FIXME: Remove this `hack_cargo_workspace` field completely once we support sysroot dependencies
+ let hack_cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") {
+ let cargo_toml = ManifestPath::try_from(
+ AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(),
+ )
+ .unwrap();
+ sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library");
+ CargoWorkspace::fetch_metadata(
+ &cargo_toml,
+ &AbsPathBuf::try_from("/").unwrap(),
+ &CargoConfig::default(),
+ &|_| (),
+ )
+ .map(CargoWorkspace::new)
+ .ok()
+ } else {
+ None
+ };
+ let mut sysroot = Sysroot {
+ root: sysroot_dir,
+ src_root: sysroot_src_dir,
+ crates: Arena::default(),
+ hack_cargo_workspace,
+ };
for path in SYSROOT_CRATES.trim().lines() {
let name = path.split('/').last().unwrap();
@@ -153,19 +194,6 @@ impl Sysroot {
}
}
- if sysroot.by_name("core").is_none() {
- let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
- " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
- } else {
- ""
- };
- tracing::error!(
- "could not find libcore in sysroot path `{}`{}",
- sysroot.src_root.as_path().display(),
- var_note,
- );
- }
-
sysroot
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
index 42c06ad0e..30ca7b348 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
@@ -16,7 +16,7 @@ pub fn get(
let mut cmd = Command::new(toolchain::rustc());
cmd.envs(extra_env);
cmd.current_dir(cargo_toml.parent())
- .args(["-Z", "unstable-options", "rustc", "--print", "target-spec-json"])
+ .args(["-Z", "unstable-options", "--print", "target-spec-json"])
.env("RUSTC_BOOTSTRAP", "1");
if let Some(target) = target {
cmd.args(["--target", target]);
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 3754accbb..7815b9dda 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -3,10 +3,11 @@ use std::{
path::{Path, PathBuf},
};
-use base_db::{CrateGraph, FileId};
+use base_db::{CrateGraph, FileId, ProcMacroPaths};
use cfg::{CfgAtom, CfgDiff};
-use expect_test::{expect, Expect};
+use expect_test::{expect_file, ExpectFile};
use paths::{AbsPath, AbsPathBuf};
+use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;
use crate::{
@@ -14,11 +15,14 @@ use crate::{
WorkspaceBuildScripts,
};
-fn load_cargo(file: &str) -> CrateGraph {
+fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
load_cargo_with_overrides(file, CfgOverrides::default())
}
-fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGraph {
+fn load_cargo_with_overrides(
+ file: &str,
+ cfg_overrides: CfgOverrides,
+) -> (CrateGraph, ProcMacroPaths) {
let meta = get_test_json_file(file);
let cargo_workspace = CargoWorkspace::new(meta);
let project_workspace = ProjectWorkspace::Cargo {
@@ -34,11 +38,39 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr
to_crate_graph(project_workspace)
}
-fn load_rust_project(file: &str) -> CrateGraph {
+fn load_cargo_with_sysroot(
+ file_map: &mut FxHashMap<AbsPathBuf, FileId>,
+ file: &str,
+) -> (CrateGraph, ProcMacroPaths) {
+ let meta = get_test_json_file(file);
+ let cargo_workspace = CargoWorkspace::new(meta);
+ let project_workspace = ProjectWorkspace::Cargo {
+ cargo: cargo_workspace,
+ build_scripts: WorkspaceBuildScripts::default(),
+ sysroot: Ok(get_fake_sysroot()),
+ rustc: Err(None),
+ rustc_cfg: Vec::new(),
+ cfg_overrides: Default::default(),
+ toolchain: None,
+ target_layout: Err("target_data_layout not loaded".into()),
+ };
+ project_workspace.to_crate_graph(
+ &mut {
+ |path| {
+ let len = file_map.len();
+ Some(*file_map.entry(path.to_path_buf()).or_insert(FileId(len as u32)))
+ }
+ },
+ &Default::default(),
+ )
+}
+
+fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
let data = get_test_json_file(file);
let project = rooted_project_json(data);
let sysroot = Ok(get_fake_sysroot());
- let project_workspace = ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new() };
+ let project_workspace =
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new(), toolchain: None };
to_crate_graph(project_workspace)
}
@@ -70,6 +102,18 @@ fn replace_root(s: &mut String, direction: bool) {
}
}
+fn replace_fake_sys_root(s: &mut String) {
+ let fake_sysroot_path = get_test_path("fake-sysroot");
+ let fake_sysroot_path = if cfg!(windows) {
+ let normalized_path =
+ fake_sysroot_path.to_str().expect("expected str").replace(r#"\"#, r#"\\"#);
+ format!(r#"{}\\"#, normalized_path)
+ } else {
+ format!("{}/", fake_sysroot_path.to_str().expect("expected str"))
+ };
+ *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$")
+}
+
fn get_test_path(file: &str) -> PathBuf {
let base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
base.join("test_data").join(file)
@@ -92,9 +136,8 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
ProjectJson::new(base, data)
}
-fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
+fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) {
project_workspace.to_crate_graph(
- &mut |_, _| Ok(Vec::new()),
&mut {
let mut counter = 0;
move |_path| {
@@ -106,1808 +149,70 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
)
}
-fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {
+fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
let mut crate_graph = format!("{crate_graph:#?}");
replace_root(&mut crate_graph, false);
+ replace_fake_sys_root(&mut crate_graph);
expect.assert_eq(&crate_graph);
}
#[test]
fn cargo_hello_world_project_model_with_wildcard_overrides() {
- let cfg_overrides = CfgOverrides::Wildcard(
- CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
- );
- let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
+ let cfg_overrides = CfgOverrides {
+ global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+ selective: Default::default(),
+ };
+ let (crate_graph, _proc_macros) =
+ load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file![
+ "../test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt"
+ ],
)
}
#[test]
fn cargo_hello_world_project_model_with_selective_overrides() {
- let cfg_overrides = {
- CfgOverrides::Selective(
- std::iter::once((
- "libc".to_owned(),
- CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
- ))
- .collect(),
- )
+ let cfg_overrides = CfgOverrides {
+ global: Default::default(),
+ selective: std::iter::once((
+ "libc".to_owned(),
+ CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+ ))
+ .collect(),
};
- let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
+ let (crate_graph, _proc_macros) =
+ load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file![
+ "../test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt"
+ ],
)
}
#[test]
fn cargo_hello_world_project_model() {
- let crate_graph = load_cargo("hello-world-metadata.json");
+ let (crate_graph, _proc_macros) = load_cargo("hello-world-metadata.json");
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file!["../test_data/output/cargo_hello_world_project_model.txt"],
)
}
#[test]
fn rust_project_hello_world_project_model() {
- let crate_graph = load_rust_project("hello-world-project.json");
+ let (crate_graph, _proc_macros) = load_rust_project("hello-world-project.json");
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "alloc",
- ),
- canonical_name: "alloc",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Alloc,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "core",
- ),
- canonical_name: "core",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Core,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "panic_abort",
- ),
- canonical_name: "panic_abort",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "panic_unwind",
- ),
- canonical_name: "panic_unwind",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "proc_macro",
- ),
- canonical_name: "proc_macro",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 6,
- ),
- name: CrateName(
- "std",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 5,
- ): CrateData {
- root_file_id: FileId(
- 6,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "profiler_builtins",
- ),
- canonical_name: "profiler_builtins",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 6,
- ): CrateData {
- root_file_id: FileId(
- 7,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "std",
- ),
- canonical_name: "std",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "alloc",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 3,
- ),
- name: CrateName(
- "panic_unwind",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 2,
- ),
- name: CrateName(
- "panic_abort",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 5,
- ),
- name: CrateName(
- "profiler_builtins",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 9,
- ),
- name: CrateName(
- "unwind",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 7,
- ),
- name: CrateName(
- "std_detect",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 8,
- ),
- name: CrateName(
- "test",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Std,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 7,
- ): CrateData {
- root_file_id: FileId(
- 8,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "std_detect",
- ),
- canonical_name: "std_detect",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 8,
- ): CrateData {
- root_file_id: FileId(
- 9,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "test",
- ),
- canonical_name: "test",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Test,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 9,
- ): CrateData {
- root_file_id: FileId(
- 10,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "unwind",
- ),
- canonical_name: "unwind",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 10,
- ): CrateData {
- root_file_id: FileId(
- 11,
- ),
- edition: Edition2018,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello_world",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "alloc",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 6,
- ),
- name: CrateName(
- "std",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 8,
- ),
- name: CrateName(
- "test",
- ),
- prelude: false,
- },
- ],
- proc_macro: Err(
- "no proc macro dylib present",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello_world",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file!["../test_data/output/rust_project_hello_world_project_model.txt"],
);
}
#[test]
fn rust_project_is_proc_macro_has_proc_macro_dep() {
- let crate_graph = load_rust_project("is-proc-macro-project.json");
+ let (crate_graph, _proc_macros) = load_rust_project("is-proc-macro-project.json");
// Since the project only defines one crate (outside the sysroot crates),
// it should be the one with the biggest Id.
let crate_id = crate_graph.iter().max().unwrap();
@@ -1916,3 +221,31 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
// on the proc_macro sysroot crate.
crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap();
}
+
+#[test]
+fn crate_graph_dedup_identical() {
+ let (mut crate_graph, proc_macros) =
+ load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json");
+ crate_graph.sort_deps();
+
+ let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
+
+ crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
+ assert!(crate_graph.iter().eq(d_crate_graph.iter()));
+ assert_eq!(proc_macros, d_proc_macros);
+}
+
+#[test]
+fn crate_graph_dedup() {
+ let path_map = &mut Default::default();
+ let (mut crate_graph, _proc_macros) =
+ load_cargo_with_sysroot(path_map, "ripgrep-metadata.json");
+ assert_eq!(crate_graph.iter().count(), 81);
+ crate_graph.sort_deps();
+ let (regex_crate_graph, mut regex_proc_macros) =
+ load_cargo_with_sysroot(path_map, "regex-metadata.json");
+ assert_eq!(regex_crate_graph.iter().count(), 60);
+
+ crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
+ assert_eq!(crate_graph.iter().count(), 118);
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index d1e53e12e..b5fe237fc 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,53 +2,43 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc};
+use std::{collections::VecDeque, fmt, fs, process::Command, sync};
use anyhow::{format_err, Context, Result};
use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
- FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult,
+ FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult,
};
use cfg::{CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
-use stdx::{always, hash::NoHashHashMap};
+use stdx::always;
+use triomphe::Arc;
use crate::{
build_scripts::BuildScriptOutput,
cargo_workspace::{DepKind, PackageData, RustLibSource},
cfg_flag::CfgFlag,
+ project_json::Crate,
rustc_cfg,
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
- Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts,
+ Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
/// A set of cfg-overrides per crate.
-///
-/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
-/// without having to first obtain a list of all crates.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum CfgOverrides {
- /// A single global set of overrides matching all crates.
- Wildcard(CfgDiff),
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+pub struct CfgOverrides {
+ /// A global set of overrides matching all crates.
+ pub global: CfgDiff,
/// A set of overrides matching specific crates.
- Selective(FxHashMap<String, CfgDiff>),
-}
-
-impl Default for CfgOverrides {
- fn default() -> Self {
- Self::Selective(FxHashMap::default())
- }
+ pub selective: FxHashMap<String, CfgDiff>,
}
impl CfgOverrides {
pub fn len(&self) -> usize {
- match self {
- CfgOverrides::Wildcard(_) => 1,
- CfgOverrides::Selective(hash_map) => hash_map.len(),
- }
+ self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::<usize>()
}
}
@@ -82,7 +72,14 @@ pub enum ProjectWorkspace {
target_layout: Result<String, String>,
},
/// Project workspace was manually specified using a `rust-project.json` file.
- Json { project: ProjectJson, sysroot: Result<Sysroot, Option<String>>, rustc_cfg: Vec<CfgFlag> },
+ Json {
+ project: ProjectJson,
+ sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
+ rustc_cfg: Vec<CfgFlag>,
+ toolchain: Option<Version>,
+ },
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
// Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
@@ -96,6 +93,8 @@ pub enum ProjectWorkspace {
DetachedFiles {
files: Vec<AbsPathBuf>,
sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
rustc_cfg: Vec<CfgFlag>,
},
}
@@ -127,12 +126,13 @@ impl fmt::Debug for ProjectWorkspace {
.field("toolchain", &toolchain)
.field("data_layout", &data_layout)
.finish(),
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
let mut debug_struct = f.debug_struct("Json");
debug_struct.field("n_crates", &project.n_crates());
if let Ok(sysroot) = sysroot {
debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
}
+ debug_struct.field("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
debug_struct.finish()
}
@@ -152,6 +152,19 @@ impl ProjectWorkspace {
config: &CargoConfig,
progress: &dyn Fn(String),
) -> Result<ProjectWorkspace> {
+ let version = |current_dir, cmd_path, prefix: &str| {
+ let cargo_version = utf8_stdout({
+ let mut cmd = Command::new(cmd_path);
+ cmd.envs(&config.extra_env);
+ cmd.arg("--version").current_dir(current_dir);
+ cmd
+ })?;
+ anyhow::Ok(
+ cargo_version
+ .get(prefix.len()..)
+ .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
+ )
+ };
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
let file = fs::read_to_string(&project_json).with_context(|| {
@@ -161,24 +174,17 @@ impl ProjectWorkspace {
format!("Failed to deserialize json file {}", project_json.display())
})?;
let project_location = project_json.parent().to_path_buf();
+ let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?;
let project_json = ProjectJson::new(&project_location, data);
ProjectWorkspace::load_inline(
project_json,
config.target.as_deref(),
&config.extra_env,
+ toolchain,
)
}
ProjectManifest::CargoToml(cargo_toml) => {
- let cargo_version = utf8_stdout({
- let mut cmd = Command::new(toolchain::cargo());
- cmd.envs(&config.extra_env);
- cmd.arg("--version");
- cmd
- })?;
- let toolchain = cargo_version
- .get("cargo ".len()..)
- .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
-
+ let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?;
let meta = CargoWorkspace::fetch_metadata(
&cargo_toml,
cargo_toml.parent(),
@@ -274,7 +280,7 @@ impl ProjectWorkspace {
let rustc_cfg =
rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
- let cfg_overrides = config.cfg_overrides();
+ let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
Some(&cargo_toml),
config.target.as_deref(),
@@ -303,6 +309,7 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ toolchain: Option<Version>,
) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
@@ -327,7 +334,7 @@ impl ProjectWorkspace {
}
let rustc_cfg = rustc_cfg::get(None, target, extra_env);
- ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }
+ ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
}
pub fn load_detached_files(
@@ -403,7 +410,7 @@ impl ProjectWorkspace {
let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
Ok(it) => Ok(it.into_iter()),
// io::Error is not Clone?
- Err(e) => Err(Arc::new(e)),
+ Err(e) => Err(sync::Arc::new(e)),
};
workspaces
@@ -440,18 +447,35 @@ impl ProjectWorkspace {
}
}
- pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
+ pub fn find_sysroot_proc_macro_srv(&self) -> Result<AbsPathBuf> {
match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
- | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => {
+ | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
+ | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
["libexec", "lib"]
.into_iter()
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
.find(|server_path| std::fs::metadata(server_path).is_ok())
+ .ok_or_else(|| {
+ anyhow::anyhow!(
+ "cannot find proc-macro server in sysroot `{}`",
+ sysroot.root().display()
+ )
+ })
+ }
+ ProjectWorkspace::DetachedFiles { .. } => {
+ Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found"))
}
- _ => None,
+ ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ cargo.workspace_root().display()
+ )),
+ ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ project.path().display()
+ )),
}
}
@@ -469,7 +493,7 @@ impl ProjectWorkspace {
})
};
match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg: _, toolchain: _ } => project
.crates()
.map(|(_, krate)| PackageRoot {
is_local: krate.is_workspace_member,
@@ -570,22 +594,23 @@ impl ProjectWorkspace {
pub fn to_crate_graph(
&self,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
extra_env: &FxHashMap<String, String>,
- ) -> CrateGraph {
+ ) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("ProjectWorkspace::to_crate_graph");
- let mut crate_graph = match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
- rustc_cfg.clone(),
- load_proc_macro,
- load,
- project,
- sysroot.as_ref().ok(),
- extra_env,
- Err("rust-project.json projects have no target layout set".into()),
- ),
+ let (mut crate_graph, proc_macros) = match self {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
+ project_json_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ project,
+ sysroot.as_ref().ok(),
+ extra_env,
+ Err("rust-project.json projects have no target layout set".into()),
+ toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
+ )
+ }
ProjectWorkspace::Cargo {
cargo,
sysroot,
@@ -593,21 +618,22 @@ impl ProjectWorkspace {
rustc_cfg,
cfg_overrides,
build_scripts,
- toolchain: _,
+ toolchain,
target_layout,
} => cargo_to_crate_graph(
- load_proc_macro,
load,
rustc.as_ref().ok(),
cargo,
sysroot.as_ref().ok(),
rustc_cfg.clone(),
cfg_overrides,
+ None,
build_scripts,
match target_layout.as_ref() {
Ok(it) => Ok(Arc::from(it.as_str())),
Err(it) => Err(Arc::from(it.as_str())),
},
+ toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
),
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
detached_files_to_crate_graph(
@@ -624,7 +650,7 @@ impl ProjectWorkspace {
} else {
tracing::debug!("Did not patch std to depend on cfg-if")
}
- crate_graph
+ (crate_graph, proc_macros)
}
pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
@@ -659,9 +685,19 @@ impl ProjectWorkspace {
&& sysroot == o_sysroot
}
(
- Self::Json { project, sysroot, rustc_cfg },
- Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
- ) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot,
+ Self::Json { project, sysroot, rustc_cfg, toolchain },
+ Self::Json {
+ project: o_project,
+ sysroot: o_sysroot,
+ rustc_cfg: o_rustc_cfg,
+ toolchain: o_toolchain,
+ },
+ ) => {
+ project == o_project
+ && rustc_cfg == o_rustc_cfg
+ && sysroot == o_sysroot
+ && toolchain == o_toolchain
+ }
(
Self::DetachedFiles { files, sysroot, rustc_cfg },
Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
@@ -669,130 +705,146 @@ impl ProjectWorkspace {
_ => false,
}
}
+
+ /// Returns `true` if the project workspace is [`Json`].
+ ///
+ /// [`Json`]: ProjectWorkspace::Json
+ #[must_use]
+ pub fn is_json(&self) -> bool {
+ matches!(self, Self::Json { .. })
+ }
}
fn project_json_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson,
sysroot: Option<&Sysroot>,
extra_env: &FxHashMap<String, String>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
- let mut crate_graph = CrateGraph::default();
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
+ let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+ let (crate_graph, proc_macros) = &mut res;
let sysroot_deps = sysroot.as_ref().map(|sysroot| {
sysroot_to_crate_graph(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
)
});
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
- let crates: NoHashHashMap<CrateId, CrateId> = project
+ let crates: FxHashMap<CrateId, CrateId> = project
.crates()
- .filter_map(|(crate_id, krate)| {
- let file_path = &krate.root_module;
- let file_id = load(file_path)?;
- Some((crate_id, krate, file_id))
- })
- .map(|(crate_id, krate, file_id)| {
- let env = krate.env.clone().into_iter().collect();
- let proc_macro = match krate.proc_macro_dylib_path.clone() {
- Some(it) => load_proc_macro(
- krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
- &it,
- ),
- None => Err("no proc macro dylib present".into()),
- };
-
- let target_cfgs = match krate.target.as_deref() {
- Some(target) => cfg_cache
- .entry(target)
- .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
- None => &rustc_cfg,
- };
-
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
- (
+ .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?)))
+ .map(
+ |(
crate_id,
- crate_graph.add_crate_root(
+ Crate {
+ display_name,
+ edition,
+ version,
+ cfg,
+ target,
+ env,
+ proc_macro_dylib_path,
+ is_proc_macro,
+ repository,
+ ..
+ },
+ file_id,
+ )| {
+ let env = env.clone().into_iter().collect();
+
+ let target_cfgs = match target.as_deref() {
+ Some(target) => cfg_cache
+ .entry(target)
+ .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
+ None => &rustc_cfg,
+ };
+
+ let crate_graph_crate_id = crate_graph.add_crate_root(
file_id,
- krate.edition,
- krate.display_name.clone(),
- krate.version.clone(),
- cfg_options.clone(),
- cfg_options,
+ *edition,
+ display_name.clone(),
+ version.clone(),
+ target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
+ None,
env,
- proc_macro,
- krate.is_proc_macro,
- if krate.display_name.is_some() {
- CrateOrigin::CratesIo {
- repo: krate.repository.clone(),
- name: krate
- .display_name
- .clone()
- .map(|n| n.canonical_name().to_string()),
+ *is_proc_macro,
+ if let Some(name) = display_name.clone() {
+ CrateOrigin::Local {
+ repo: repository.clone(),
+ name: Some(name.canonical_name().to_string()),
}
} else {
- CrateOrigin::CratesIo { repo: None, name: None }
+ CrateOrigin::Local { repo: None, name: None }
},
target_layout.clone(),
- ),
- )
- })
+ channel,
+ );
+ if *is_proc_macro {
+ if let Some(path) = proc_macro_dylib_path.clone() {
+ let node = Ok((
+ display_name.as_ref().map(|it| it.canonical_name().to_owned()),
+ path,
+ ));
+ proc_macros.insert(crate_graph_crate_id, node);
+ }
+ }
+ (crate_id, crate_graph_crate_id)
+ },
+ )
.collect();
for (from, krate) in project.crates() {
if let Some(&from) = crates.get(&from) {
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
- public_deps.add_to_crate_graph(&mut crate_graph, from);
- if krate.is_proc_macro {
- if let Some(proc_macro) = libproc_macro {
- add_dep(
- &mut crate_graph,
- from,
- CrateName::new("proc_macro").unwrap(),
- *proc_macro,
- );
- }
+ public_deps.add_to_crate_graph(crate_graph, from);
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, from, *proc_macro, krate.is_proc_macro);
}
}
for dep in &krate.deps {
if let Some(&to) = crates.get(&dep.crate_id) {
- add_dep(&mut crate_graph, from, dep.name.clone(), to)
+ add_dep(crate_graph, from, dep.name.clone(), to)
}
}
}
}
- crate_graph
+ res
}
fn cargo_to_crate_graph(
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
cargo: &CargoWorkspace,
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
+ // Don't compute cfg and use this if present
+ forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("cargo_to_crate_graph");
- let mut crate_graph = CrateGraph::default();
+ let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+ let crate_graph = &mut res.0;
+ let proc_macros = &mut res.1;
let (public_deps, libproc_macro) = match sysroot {
Some(sysroot) => sysroot_to_crate_graph(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -804,37 +856,40 @@ fn cargo_to_crate_graph(
cfg_options
};
+ // Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
-
let mut pkg_crates = FxHashMap::default();
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
let mut has_private = false;
+
// Next, create crates for each package, target pair
for pkg in cargo.packages() {
- let mut cfg_options = cfg_options.clone();
+ has_private |= cargo[pkg].metadata.rustc_private;
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
- };
+ let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
+ let mut cfg_options = cfg_options.clone();
- // Add test cfg for local crates
- if cargo[pkg].is_local {
- cfg_options.insert_atom("test".into());
- }
+ // Add test cfg for local crates
+ if cargo[pkg].is_local {
+ cfg_options.insert_atom("test".into());
+ }
- if let Some(overrides) = overrides {
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
- //
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
- // figure out the set of cfgs that would have to apply to make it active.
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
+ };
+ if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) {
+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
+ //
+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
+ // figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
- };
+ cfg_options.apply_diff(diff.clone());
+ };
+ cfg_options
+ });
- has_private |= cargo[pkg].metadata.rustc_private;
let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() {
if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
@@ -845,44 +900,57 @@ fn cargo_to_crate_graph(
// https://github.com/rust-lang/rust-analyzer/issues/11300
continue;
}
+ let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt];
- if let Some(file_id) = load(&cargo[tgt].root) {
- let crate_id = add_target_crate_root(
- &mut crate_graph,
- &cargo[pkg],
- build_scripts.get_output(pkg),
- cfg_options.clone(),
- &mut |path| load_proc_macro(&cargo[tgt].name, path),
- file_id,
- &cargo[tgt].name,
- cargo[tgt].is_proc_macro,
- target_layout.clone(),
- );
- if cargo[tgt].kind == TargetKind::Lib {
- lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
+ if kind == TargetKind::Lib
+ && sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root()))
+ {
+ if let Some(&(_, crate_id, _)) =
+ public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name)
+ {
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
+
+ lib_tgt = Some((crate_id, name.clone()));
pkg_to_lib_crate.insert(pkg, crate_id);
+ // sysroot is inside the workspace, prevent the sysroot crates from being duplicated here
+ continue;
}
- // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
- // (just none of the APIs work when called outside of a proc macro).
- if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
- &mut crate_graph,
- crate_id,
- CrateName::new("proc_macro").unwrap(),
- proc_macro,
- cargo[tgt].is_proc_macro,
- );
- }
+ }
+
+ let Some(file_id) = load(root) else { continue };
- pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
+ let crate_id = add_target_crate_root(
+ crate_graph,
+ proc_macros,
+ &cargo[pkg],
+ build_scripts.get_output(pkg),
+ cfg_options.clone(),
+ file_id,
+ name,
+ is_proc_macro,
+ target_layout.clone(),
+ false,
+ channel,
+ );
+ if kind == TargetKind::Lib {
+ lib_tgt = Some((crate_id, name.clone()));
+ pkg_to_lib_crate.insert(pkg, crate_id);
+ }
+ // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
+ // (just none of the APIs work when called outside of a proc macro).
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro);
}
+
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
}
// Set deps to the core, std and to the lib target of the current package
for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
- public_deps.add_to_crate_graph(&mut crate_graph, from);
+ public_deps.add_to_crate_graph(crate_graph, from);
+ // Add dep edge of all targets to the package's lib target
if let Some((to, name)) = lib_tgt.clone() {
if to != from && kind != TargetKind::BuildScript {
// (build script can not depend on its library target)
@@ -891,7 +959,7 @@ fn cargo_to_crate_graph(
// cargo metadata does not do any normalization,
// so we do it ourselves currently
let name = CrateName::normalize_dashes(&name);
- add_dep(&mut crate_graph, from, name, to);
+ add_dep(crate_graph, from, name, to);
}
}
}
@@ -900,21 +968,18 @@ fn cargo_to_crate_graph(
// Now add a dep edge from all targets of upstream to the lib
// target of downstream.
for pkg in cargo.packages() {
- for dep in cargo[pkg].dependencies.iter() {
- let name = CrateName::new(&dep.name).unwrap();
- if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
- for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
- if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
- // Only build scripts may depend on build dependencies.
- continue;
- }
- if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
- // Build scripts may only depend on build dependencies.
- continue;
- }
+ for dep in &cargo[pkg].dependencies {
+ let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue };
+ let Some(targets) = pkg_crates.get(&pkg) else { continue };
- add_dep(&mut crate_graph, from, name.clone(), to)
+ let name = CrateName::new(&dep.name).unwrap();
+ for &(from, kind) in targets {
+ // Build scripts may only depend on build dependencies.
+ if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) {
+ continue;
}
+
+ add_dep(crate_graph, from, name.clone(), to)
}
}
}
@@ -924,10 +989,10 @@ fn cargo_to_crate_graph(
// and create dependencies on them for the crates which opt-in to that
if let Some((rustc_workspace, rustc_build_scripts)) = rustc {
handle_rustc_crates(
- &mut crate_graph,
+ crate_graph,
+ proc_macros,
&mut pkg_to_lib_crate,
load,
- load_proc_macro,
rustc_workspace,
cargo,
&public_deps,
@@ -943,10 +1008,11 @@ fn cargo_to_crate_graph(
rustc_build_scripts
},
target_layout,
+ channel,
);
}
}
- crate_graph
+ res
}
fn detached_files_to_crate_graph(
@@ -955,7 +1021,7 @@ fn detached_files_to_crate_graph(
detached_files: &[AbsPathBuf],
sysroot: Option<&Sysroot>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("detached_files_to_crate_graph");
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) = match sysroot {
@@ -965,6 +1031,7 @@ fn detached_files_to_crate_graph(
rustc_cfg.clone(),
target_layout.clone(),
load,
+ None,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -990,27 +1057,27 @@ fn detached_files_to_crate_graph(
display_name.clone(),
None,
cfg_options.clone(),
- cfg_options.clone(),
+ None,
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo {
+ CrateOrigin::Local {
repo: None,
name: display_name.map(|n| n.canonical_name().to_string()),
},
target_layout.clone(),
+ None,
);
public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
}
- crate_graph
+ (crate_graph, FxHashMap::default())
}
fn handle_rustc_crates(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
rustc_workspace: &CargoWorkspace,
cargo: &CargoWorkspace,
public_deps: &SysrootPublicDeps,
@@ -1020,6 +1087,7 @@ fn handle_rustc_crates(
override_cfg: &CfgOverrides,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
+ channel: Option<ReleaseChannel>,
) {
let mut rustc_pkg_crates = FxHashMap::default();
// The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1044,14 +1112,10 @@ fn handle_rustc_crates(
let mut cfg_options = cfg_options.clone();
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => {
- cfg_overrides.get(&rustc_workspace[pkg].name)
- }
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
};
-
- if let Some(overrides) = overrides {
+ if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) {
// FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
// in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
// working on rust-lang/rust as that's the only time it appears outside sysroot).
@@ -1059,7 +1123,7 @@ fn handle_rustc_crates(
// A more ideal solution might be to reanalyze crates based on where the cursor is and
// figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
+ cfg_options.apply_diff(diff.clone());
};
for &tgt in rustc_workspace[pkg].targets.iter() {
@@ -1069,23 +1133,24 @@ fn handle_rustc_crates(
if let Some(file_id) = load(&rustc_workspace[tgt].root) {
let crate_id = add_target_crate_root(
crate_graph,
+ proc_macros,
&rustc_workspace[pkg],
build_scripts.get_output(pkg),
cfg_options.clone(),
- &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
file_id,
&rustc_workspace[tgt].name,
rustc_workspace[tgt].is_proc_macro,
target_layout.clone(),
+ true,
+ channel,
);
pkg_to_lib_crate.insert(pkg, crate_id);
// Add dependencies on core / std / alloc for this crate
public_deps.add_to_crate_graph(crate_graph, crate_id);
if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
+ add_proc_macro_dep(
crate_graph,
crate_id,
- CrateName::new("proc_macro").unwrap(),
proc_macro,
rustc_workspace[tgt].is_proc_macro,
);
@@ -1134,22 +1199,29 @@ fn handle_rustc_crates(
fn add_target_crate_root(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg: &PackageData,
build_data: Option<&BuildScriptOutput>,
cfg_options: CfgOptions,
- load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
file_id: FileId,
cargo_name: &str,
is_proc_macro: bool,
target_layout: TargetLayoutLoadResult,
+ rustc_crate: bool,
+ channel: Option<ReleaseChannel>,
) -> CrateId {
let edition = pkg.edition;
- let mut potential_cfg_options = cfg_options.clone();
- potential_cfg_options.extend(
- pkg.features
- .iter()
- .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
- );
+ let potential_cfg_options = if pkg.features.is_empty() {
+ None
+ } else {
+ let mut potential_cfg_options = cfg_options.clone();
+ potential_cfg_options.extend(
+ pkg.features
+ .iter()
+ .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
+ );
+ Some(potential_cfg_options)
+ };
let cfg_options = {
let mut opts = cfg_options;
for feature in pkg.active_features.iter() {
@@ -1170,14 +1242,8 @@ fn add_target_crate_root(
}
}
- let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
- Some(Some(it)) => load_proc_macro(it),
- Some(None) => Err("no proc macro dylib present".into()),
- None => Err("crate has not (yet) been built".into()),
- };
-
let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
- crate_graph.add_crate_root(
+ let crate_id = crate_graph.add_crate_root(
file_id,
edition,
Some(display_name),
@@ -1185,11 +1251,28 @@ fn add_target_crate_root(
cfg_options,
potential_cfg_options,
env,
- proc_macro,
is_proc_macro,
- CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
+ if rustc_crate {
+ CrateOrigin::Rustc { name: pkg.name.clone() }
+ } else if pkg.is_member {
+ CrateOrigin::Local { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }
+ } else {
+ CrateOrigin::Library { repo: pkg.repository.clone(), name: pkg.name.clone() }
+ },
target_layout,
- )
+ channel,
+ );
+ if is_proc_macro {
+ let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
+ Some(it) => it.cloned().map(|path| Ok((Some(cargo_name.to_owned()), path))),
+ None => Some(Err("crate has not yet been built".to_owned())),
+ };
+ if let Some(proc_macro) = proc_macro {
+ proc_macros.insert(crate_id, proc_macro);
+ }
+ }
+
+ crate_id
}
#[derive(Default)]
@@ -1212,34 +1295,47 @@ fn sysroot_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
target_layout: TargetLayoutLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ channel: Option<ReleaseChannel>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
- let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
- .crates()
- .filter_map(|krate| {
- let file_id = load(&sysroot[krate].root)?;
-
- let env = Env::default();
- let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
- let crate_id = crate_graph.add_crate_root(
- file_id,
- Edition::CURRENT,
- Some(display_name),
- None,
- cfg_options.clone(),
- cfg_options.clone(),
- env,
- Err("no proc macro loaded for sysroot crate".into()),
- false,
- CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
- target_layout.clone(),
- );
- Some((krate, crate_id))
- })
- .collect();
-
+ cfg_options.extend(rustc_cfg.clone());
+ let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
+ Some(cargo) => handle_hack_cargo_workspace(
+ load,
+ cargo,
+ rustc_cfg,
+ cfg_options,
+ target_layout,
+ channel,
+ crate_graph,
+ sysroot,
+ ),
+ None => sysroot
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&sysroot[krate].root)?;
+
+ let env = Env::default();
+ let display_name =
+ CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
+ let crate_id = crate_graph.add_crate_root(
+ file_id,
+ Edition::CURRENT,
+ Some(display_name),
+ None,
+ cfg_options.clone(),
+ None,
+ env,
+ false,
+ CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
+ target_layout.clone(),
+ channel,
+ );
+ Some((krate, crate_id))
+ })
+ .collect(),
+ };
for from in sysroot.crates() {
for &to in sysroot[from].deps.iter() {
let name = CrateName::new(&sysroot[to].name).unwrap();
@@ -1260,6 +1356,69 @@ fn sysroot_to_crate_graph(
(public_deps, libproc_macro)
}
+fn handle_hack_cargo_workspace(
+ load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ cargo: &CargoWorkspace,
+ rustc_cfg: Vec<CfgFlag>,
+ cfg_options: CfgOptions,
+ target_layout: Result<Arc<str>, Arc<str>>,
+ channel: Option<ReleaseChannel>,
+ crate_graph: &mut CrateGraph,
+ sysroot: &Sysroot,
+) -> FxHashMap<SysrootCrate, CrateId> {
+ let (cg, mut pm) = cargo_to_crate_graph(
+ load,
+ None,
+ cargo,
+ None,
+ rustc_cfg,
+ &CfgOverrides::default(),
+ Some(cfg_options),
+ &WorkspaceBuildScripts::default(),
+ target_layout,
+ channel,
+ );
+ crate_graph.extend(cg, &mut pm);
+ for crate_name in ["std", "alloc", "core"] {
+ let original = crate_graph
+ .iter()
+ .find(|x| {
+ crate_graph[*x]
+ .display_name
+ .as_ref()
+ .map(|x| x.canonical_name() == crate_name)
+ .unwrap_or(false)
+ })
+ .unwrap();
+ let fake_crate_name = format!("rustc-std-workspace-{}", crate_name);
+ let fake = crate_graph
+ .iter()
+ .find(|x| {
+ crate_graph[*x]
+ .display_name
+ .as_ref()
+ .map(|x| x.canonical_name() == fake_crate_name)
+ .unwrap_or(false)
+ })
+ .unwrap();
+ crate_graph.remove_and_replace(fake, original).unwrap();
+ }
+ for (_, c) in crate_graph.iter_mut() {
+ if c.origin.is_local() {
+ // LangCrateOrigin::Other is good enough for a hack.
+ c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
+ }
+ }
+ sysroot
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&sysroot[krate].root)?;
+ let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
+ Some((krate, crate_id))
+ })
+ .collect()
+}
+
fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
add_dep_inner(graph, from, Dependency::new(name, to))
}
@@ -1274,6 +1433,10 @@ fn add_dep_with_prelude(
add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
}
+fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) {
+ add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude);
+}
+
fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
if let Err(err) = graph.add_dep(from, dep) {
tracing::error!("{}", err)
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
new file mode 100644
index 000000000..e595cd827
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -0,0 +1,344 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
new file mode 100644
index 000000000..e595cd827
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -0,0 +1,344 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
new file mode 100644
index 000000000..f10c55d04
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -0,0 +1,340 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
new file mode 100644
index 000000000..fb3f5933b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -0,0 +1,462 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "alloc",
+ ),
+ canonical_name: "alloc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Alloc,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "core",
+ ),
+ canonical_name: "core",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Core,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "panic_abort",
+ ),
+ canonical_name: "panic_abort",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "panic_unwind",
+ ),
+ canonical_name: "panic_unwind",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "proc_macro",
+ ),
+ canonical_name: "proc_macro",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(6),
+ name: CrateName(
+ "std",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 5: CrateData {
+ root_file_id: FileId(
+ 6,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "profiler_builtins",
+ ),
+ canonical_name: "profiler_builtins",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 6: CrateData {
+ root_file_id: FileId(
+ 7,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "std",
+ ),
+ canonical_name: "std",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "alloc",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(3),
+ name: CrateName(
+ "panic_unwind",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(2),
+ name: CrateName(
+ "panic_abort",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(5),
+ name: CrateName(
+ "profiler_builtins",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(9),
+ name: CrateName(
+ "unwind",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(7),
+ name: CrateName(
+ "std_detect",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(8),
+ name: CrateName(
+ "test",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Std,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 7: CrateData {
+ root_file_id: FileId(
+ 8,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "std_detect",
+ ),
+ canonical_name: "std_detect",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 8: CrateData {
+ root_file_id: FileId(
+ 9,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "test",
+ ),
+ canonical_name: "test",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Test,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 9: CrateData {
+ root_file_id: FileId(
+ 10,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "unwind",
+ ),
+ canonical_name: "unwind",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 10: CrateData {
+ root_file_id: FileId(
+ 11,
+ ),
+ edition: Edition2018,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello_world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "alloc",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(6),
+ name: CrateName(
+ "std",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(8),
+ name: CrateName(
+ "test",
+ ),
+ prelude: false,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "proc_macro",
+ ),
+ prelude: false,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello_world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
new file mode 100644
index 000000000..371464dd2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
@@ -0,0 +1,6420 @@
+{
+ "packages": [
+ {
+ "name": "aho-corasick",
+ "version": "0.7.20",
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": [
+ "memchr/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "aho",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cc",
+ "version": "1.0.79",
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jobserver",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gcc-shim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cc_env",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cxxflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "jobserver": [
+ "dep:jobserver"
+ ],
+ "parallel": [
+ "jobserver"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::build-utils"
+ ],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/cc-rs",
+ "homepage": "https://github.com/rust-lang/cc-rs",
+ "documentation": "https://docs.rs/cc",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "0.1.10",
+ "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "1.0.0",
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "docopt",
+ "version": "1.1.1",
+ "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Command line argument parsing.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std",
+ "unicode"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "strsim",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "docopt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "docopt-wordlist",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cargo",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cp",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "decode",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "hashmap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "optional_command",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "verbose_multiple",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "command-line-interface"
+ ],
+ "keywords": [
+ "docopt",
+ "argument",
+ "command",
+ "argv"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/docopt/docopt.rs",
+ "homepage": "https://github.com/docopt/docopt.rs",
+ "documentation": "http://burntsushi.net/rustdoc/docopt/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "getrandom",
+ "version": "0.2.9",
+ "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A small cross-platform library for retrieving random data from system source",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "js-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasm-bindgen",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.62",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasm-bindgen-test",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.18",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(target_os = \"wasi\")",
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.139",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "getrandom",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "custom",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "normal",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rdrand",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "custom": [],
+ "js": [
+ "wasm-bindgen",
+ "js-sys"
+ ],
+ "js-sys": [
+ "dep:js-sys"
+ ],
+ "rdrand": [],
+ "rustc-dep-of-std": [
+ "compiler_builtins",
+ "core",
+ "libc/rustc-dep-of-std",
+ "wasi/rustc-dep-of-std"
+ ],
+ "std": [],
+ "test-in-browser": [],
+ "wasm-bindgen": [
+ "dep:wasm-bindgen"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "std",
+ "custom"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers"
+ ],
+ "categories": [
+ "os",
+ "no-std"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/getrandom",
+ "homepage": null,
+ "documentation": "https://docs.rs/getrandom",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "lazy_static",
+ "version": "1.4.0",
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro for declaring lazily evaluated statics in Rust.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "spin",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "no_std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "spin": [
+ "dep:spin"
+ ],
+ "spin_no_std": [
+ "spin"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Marvin Löbel <loebel.marvin@gmail.com>"
+ ],
+ "categories": [
+ "no-std",
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "macro",
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/lazy_static",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "libc",
+ "version": "0.2.142",
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings to platform libraries like libc.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "libc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "const_fn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "align": [],
+ "const-extern-fn": [],
+ "default": [
+ "std"
+ ],
+ "extra_traits": [],
+ "rustc-dep-of-std": [
+ "align",
+ "rustc-std-workspace-core"
+ ],
+ "rustc-std-workspace-core": [
+ "dep:rustc-std-workspace-core"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "const-extern-fn",
+ "extra_traits"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os"
+ ],
+ "keywords": [
+ "libc",
+ "ffi",
+ "bindings",
+ "operating",
+ "system"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/libc",
+ "homepage": "https://github.com/rust-lang/libc",
+ "documentation": "https://docs.rs/libc/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memchr",
+ "version": "2.5.0",
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Safe interface to memchr.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memchr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>",
+ "bluss"
+ ],
+ "categories": [],
+ "keywords": [
+ "memchr",
+ "char",
+ "scan",
+ "strchr",
+ "string"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/memchr",
+ "homepage": "https://github.com/BurntSushi/memchr",
+ "documentation": "https://docs.rs/memchr/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memmap",
+ "version": "0.6.2",
+ "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Cross-platform Rust API for memory-mapped file IO",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "tempdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ },
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "basetsd",
+ "handleapi",
+ "memoryapi",
+ "minwindef",
+ "std",
+ "sysinfoapi"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memmap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Dan Burkert <dan@danburkert.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "mmap",
+ "memory-map",
+ "io",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/danburkert/memmap-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/memmap",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pkg-config",
+ "version": "0.3.26",
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pkg-config",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/pkg-config-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/pkg-config",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "proc-macro2",
+ "version": "1.0.56",
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "proc-macro2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "comments",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "features",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "marker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_fmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "nightly": [],
+ "proc-macro": [],
+ "span-locations": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt",
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "span-locations"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>",
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/proc-macro2",
+ "homepage": null,
+ "documentation": "https://docs.rs/proc-macro2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "quickcheck",
+ "version": "1.0.3",
+ "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Automatic property based testing with shrinking.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "env_logger",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quickcheck",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "btree_set_range",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "out_of_bounds",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reverse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reverse_single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sieve",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sort",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "regex",
+ "use_logging"
+ ],
+ "env_logger": [
+ "dep:env_logger"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "regex": [
+ "env_logger/regex"
+ ],
+ "use_logging": [
+ "log",
+ "env_logger"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::testing"
+ ],
+ "keywords": [
+ "testing",
+ "quickcheck",
+ "property",
+ "shrinking",
+ "fuzz"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/quickcheck",
+ "homepage": "https://github.com/BurntSushi/quickcheck",
+ "documentation": "https://docs.rs/quickcheck",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "quote",
+ "version": "1.0.26",
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Quasi-quoting macro quote!(...)",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.52",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.66",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quote",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/quote",
+ "homepage": null,
+ "documentation": "https://docs.rs/quote/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "rand",
+ "version": "0.8.5",
+ "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Random number generators and other randomness functionality.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.4",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.7",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "into_bits"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_chacha",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.103",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bincode",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_pcg",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.22",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "rand",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [
+ "rand_core/alloc"
+ ],
+ "default": [
+ "std",
+ "std_rng"
+ ],
+ "getrandom": [
+ "rand_core/getrandom"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "min_const_gen": [],
+ "nightly": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "rand_chacha": [
+ "dep:rand_chacha"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde",
+ "rand_core/serde1"
+ ],
+ "simd_support": [
+ "packed_simd"
+ ],
+ "small_rng": [],
+ "std": [
+ "rand_core/std",
+ "rand_chacha/std",
+ "alloc",
+ "getrandom",
+ "libc"
+ ],
+ "std_rng": [
+ "rand_chacha"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "small_rng",
+ "serde1"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers",
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [
+ "random",
+ "rng"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/rand",
+ "homepage": "https://rust-random.github.io/book",
+ "documentation": "https://docs.rs/rand",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "rand_core",
+ "version": "0.6.4",
+ "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Core random number generator traits and tools for implementation.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "getrandom",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "rand_core",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "getrandom": [
+ "dep:getrandom"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde"
+ ],
+ "std": [
+ "alloc",
+ "getrandom",
+ "getrandom/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ]
+ }
+ },
+ "playground": {
+ "all-features": true
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers",
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [
+ "random",
+ "rng"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/rand",
+ "homepage": "https://rust-random.github.io/book",
+ "documentation": "https://docs.rs/rand_core",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex",
+ "version": "1.7.1",
+ "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6.27",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$regex/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$regex/tests/test_default.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$regex/tests/test_default_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$regex/tests/test_nfa.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$regex/tests/test_backtrack.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$regex/tests/test_crates_regex.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$regex/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex",
+ "version": "1.8.1",
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "regex-benchmark",
+ "version": "0.1.0",
+ "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Regex benchmarks for Rust's and other engines.",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "docopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libpcre-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memmap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "onig",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pkg-config",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.9",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex-run-one",
+ "src_path": "$ROOT$regex/bench/src/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$regex/bench/src/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$regex/bench/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "libpcre-sys": [
+ "dep:libpcre-sys"
+ ],
+ "onig": [
+ "dep:onig"
+ ],
+ "re-onig": [
+ "onig"
+ ],
+ "re-pcre1": [
+ "libpcre-sys"
+ ],
+ "re-pcre2": [],
+ "re-re2": [],
+ "re-rust": [],
+ "re-rust-bytes": [],
+ "re-tcl": []
+ },
+ "manifest_path": "$ROOT$regex/bench/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-debug",
+ "version": "0.1.0",
+ "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A tool useful for debugging regular expressions.",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "docopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex-debug",
+ "src_path": "$ROOT$regex/regex-debug/src/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.6.28",
+ "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": null,
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$regex/regex-syntax/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "unicode"
+ ],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.7.1",
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "rure",
+ "version": "0.2.2",
+ "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A C API for Rust's regular expression library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "staticlib",
+ "cdylib",
+ "rlib"
+ ],
+ "crate_types": [
+ "staticlib",
+ "cdylib",
+ "rlib"
+ ],
+ "name": "rure",
+ "src_path": "$ROOT$regex/regex-capi/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "serde",
+ "version": "1.0.160",
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A generic serialization/deserialization framework",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.160",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "derive": [
+ "serde_derive"
+ ],
+ "rc": [],
+ "serde_derive": [
+ "dep:serde_derive"
+ ],
+ "std": [],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "derive"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "derive",
+ "rc"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://docs.rs/serde",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.19"
+ },
+ {
+ "name": "serde_derive",
+ "version": "1.0.160",
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "proc-macro"
+ ],
+ "crate_types": [
+ "proc-macro"
+ ],
+ "name": "serde_derive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [],
+ "deserialize_in_place": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std",
+ "derive"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://serde.rs/derive.html",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "strsim",
+ "version": "0.10.0",
+ "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "strsim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lib",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benches",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Danny Guo <danny@dannyguo.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "string",
+ "similarity",
+ "Hamming",
+ "Levenshtein",
+ "Jaro"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dguo/strsim-rs",
+ "homepage": "https://github.com/dguo/strsim-rs",
+ "documentation": "https://docs.rs/strsim/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "syn",
+ "version": "2.0.15",
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Parser for Rust source code",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.55",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.25",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "anyhow",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "flate2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "insta",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rayon",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "reqwest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "blocking"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn-test-suite",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.16",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "syn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_asyncness",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_attribute",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_derive_input",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_expr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_generics",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_grouping",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_item",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_iterators",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_lit",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_meta",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_pat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_path",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_precedence",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_receiver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_round_trip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_shebang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_should_parse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_stmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_token_trees",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_visibility",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zzz_stable",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rust",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "clone-impls": [],
+ "default": [
+ "derive",
+ "parsing",
+ "printing",
+ "clone-impls",
+ "proc-macro"
+ ],
+ "derive": [],
+ "extra-traits": [],
+ "fold": [],
+ "full": [],
+ "parsing": [],
+ "printing": [
+ "quote"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro",
+ "quote/proc-macro"
+ ],
+ "quote": [
+ "dep:quote"
+ ],
+ "test": [
+ "syn-test-suite/all-features"
+ ],
+ "visit": [],
+ "visit-mut": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "full",
+ "visit",
+ "visit-mut",
+ "fold",
+ "extra-traits"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "parser-implementations"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/syn",
+ "homepage": null,
+ "documentation": "https://docs.rs/syn",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "unicode-ident",
+ "version": "1.0.8",
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+ "license_file": null,
+ "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "roaring",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-trie",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-xid",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compare",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "static_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xid",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "no-std"
+ ],
+ "keywords": [
+ "unicode",
+ "xid"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/unicode-ident",
+ "homepage": null,
+ "documentation": "https://docs.rs/unicode-ident",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "wasi",
+ "version": "0.11.0+wasi-snapshot-preview1",
+ "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT",
+ "license_file": null,
+ "description": "Experimental WASI API bindings for Rust",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-alloc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "wasi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "rustc-dep-of-std": [
+ "compiler_builtins",
+ "core",
+ "rustc-std-workspace-alloc"
+ ],
+ "rustc-std-workspace-alloc": [
+ "dep:rustc-std-workspace-alloc"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Cranelift Project Developers"
+ ],
+ "categories": [
+ "no-std",
+ "wasm"
+ ],
+ "keywords": [
+ "webassembly",
+ "wasm"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/bytecodealliance/wasi",
+ "homepage": null,
+ "documentation": "https://docs.rs/wasi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi",
+ "version": "0.3.9",
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings for all of Windows API.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "i686-pc-windows-gnu",
+ "registry": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "x86_64-pc-windows-gnu",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "accctrl": [],
+ "aclapi": [],
+ "activation": [],
+ "adhoc": [],
+ "appmgmt": [],
+ "audioclient": [],
+ "audiosessiontypes": [],
+ "avrt": [],
+ "basetsd": [],
+ "bcrypt": [],
+ "bits": [],
+ "bits10_1": [],
+ "bits1_5": [],
+ "bits2_0": [],
+ "bits2_5": [],
+ "bits3_0": [],
+ "bits4_0": [],
+ "bits5_0": [],
+ "bitscfg": [],
+ "bitsmsg": [],
+ "bluetoothapis": [],
+ "bluetoothleapis": [],
+ "bthdef": [],
+ "bthioctl": [],
+ "bthledef": [],
+ "bthsdpdef": [],
+ "bugcodes": [],
+ "cderr": [],
+ "cfg": [],
+ "cfgmgr32": [],
+ "cguid": [],
+ "combaseapi": [],
+ "coml2api": [],
+ "commapi": [],
+ "commctrl": [],
+ "commdlg": [],
+ "commoncontrols": [],
+ "consoleapi": [],
+ "corecrt": [],
+ "corsym": [],
+ "d2d1": [],
+ "d2d1_1": [],
+ "d2d1_2": [],
+ "d2d1_3": [],
+ "d2d1effectauthor": [],
+ "d2d1effects": [],
+ "d2d1effects_1": [],
+ "d2d1effects_2": [],
+ "d2d1svg": [],
+ "d2dbasetypes": [],
+ "d3d": [],
+ "d3d10": [],
+ "d3d10_1": [],
+ "d3d10_1shader": [],
+ "d3d10effect": [],
+ "d3d10misc": [],
+ "d3d10sdklayers": [],
+ "d3d10shader": [],
+ "d3d11": [],
+ "d3d11_1": [],
+ "d3d11_2": [],
+ "d3d11_3": [],
+ "d3d11_4": [],
+ "d3d11on12": [],
+ "d3d11sdklayers": [],
+ "d3d11shader": [],
+ "d3d11tokenizedprogramformat": [],
+ "d3d12": [],
+ "d3d12sdklayers": [],
+ "d3d12shader": [],
+ "d3d9": [],
+ "d3d9caps": [],
+ "d3d9types": [],
+ "d3dcommon": [],
+ "d3dcompiler": [],
+ "d3dcsx": [],
+ "d3dkmdt": [],
+ "d3dkmthk": [],
+ "d3dukmdt": [],
+ "d3dx10core": [],
+ "d3dx10math": [],
+ "d3dx10mesh": [],
+ "datetimeapi": [],
+ "davclnt": [],
+ "dbghelp": [],
+ "dbt": [],
+ "dcommon": [],
+ "dcomp": [],
+ "dcompanimation": [],
+ "dcomptypes": [],
+ "dde": [],
+ "ddraw": [],
+ "ddrawi": [],
+ "ddrawint": [],
+ "debug": [
+ "impl-debug"
+ ],
+ "debugapi": [],
+ "devguid": [],
+ "devicetopology": [],
+ "devpkey": [],
+ "devpropdef": [],
+ "dinput": [],
+ "dinputd": [],
+ "dispex": [],
+ "dmksctl": [],
+ "dmusicc": [],
+ "docobj": [],
+ "documenttarget": [],
+ "dot1x": [],
+ "dpa_dsa": [],
+ "dpapi": [],
+ "dsgetdc": [],
+ "dsound": [],
+ "dsrole": [],
+ "dvp": [],
+ "dwmapi": [],
+ "dwrite": [],
+ "dwrite_1": [],
+ "dwrite_2": [],
+ "dwrite_3": [],
+ "dxdiag": [],
+ "dxfile": [],
+ "dxgi": [],
+ "dxgi1_2": [],
+ "dxgi1_3": [],
+ "dxgi1_4": [],
+ "dxgi1_5": [],
+ "dxgi1_6": [],
+ "dxgidebug": [],
+ "dxgiformat": [],
+ "dxgitype": [],
+ "dxva2api": [],
+ "dxvahd": [],
+ "eaptypes": [],
+ "enclaveapi": [],
+ "endpointvolume": [],
+ "errhandlingapi": [],
+ "everything": [],
+ "evntcons": [],
+ "evntprov": [],
+ "evntrace": [],
+ "excpt": [],
+ "exdisp": [],
+ "fibersapi": [],
+ "fileapi": [],
+ "functiondiscoverykeys_devpkey": [],
+ "gl-gl": [],
+ "guiddef": [],
+ "handleapi": [],
+ "heapapi": [],
+ "hidclass": [],
+ "hidpi": [],
+ "hidsdi": [],
+ "hidusage": [],
+ "highlevelmonitorconfigurationapi": [],
+ "hstring": [],
+ "http": [],
+ "ifdef": [],
+ "ifmib": [],
+ "imm": [],
+ "impl-debug": [],
+ "impl-default": [],
+ "in6addr": [],
+ "inaddr": [],
+ "inspectable": [],
+ "interlockedapi": [],
+ "intsafe": [],
+ "ioapiset": [],
+ "ipexport": [],
+ "iphlpapi": [],
+ "ipifcons": [],
+ "ipmib": [],
+ "iprtrmib": [],
+ "iptypes": [],
+ "jobapi": [],
+ "jobapi2": [],
+ "knownfolders": [],
+ "ks": [],
+ "ksmedia": [],
+ "ktmtypes": [],
+ "ktmw32": [],
+ "l2cmn": [],
+ "libloaderapi": [],
+ "limits": [],
+ "lmaccess": [],
+ "lmalert": [],
+ "lmapibuf": [],
+ "lmat": [],
+ "lmcons": [],
+ "lmdfs": [],
+ "lmerrlog": [],
+ "lmjoin": [],
+ "lmmsg": [],
+ "lmremutl": [],
+ "lmrepl": [],
+ "lmserver": [],
+ "lmshare": [],
+ "lmstats": [],
+ "lmsvc": [],
+ "lmuse": [],
+ "lmwksta": [],
+ "lowlevelmonitorconfigurationapi": [],
+ "lsalookup": [],
+ "memoryapi": [],
+ "minschannel": [],
+ "minwinbase": [],
+ "minwindef": [],
+ "mmdeviceapi": [],
+ "mmeapi": [],
+ "mmreg": [],
+ "mmsystem": [],
+ "mprapidef": [],
+ "msaatext": [],
+ "mscat": [],
+ "mschapp": [],
+ "mssip": [],
+ "mstcpip": [],
+ "mswsock": [],
+ "mswsockdef": [],
+ "namedpipeapi": [],
+ "namespaceapi": [],
+ "nb30": [],
+ "ncrypt": [],
+ "netioapi": [],
+ "nldef": [],
+ "ntddndis": [],
+ "ntddscsi": [],
+ "ntddser": [],
+ "ntdef": [],
+ "ntlsa": [],
+ "ntsecapi": [],
+ "ntstatus": [],
+ "oaidl": [],
+ "objbase": [],
+ "objidl": [],
+ "objidlbase": [],
+ "ocidl": [],
+ "ole2": [],
+ "oleauto": [],
+ "olectl": [],
+ "oleidl": [],
+ "opmapi": [],
+ "pdh": [],
+ "perflib": [],
+ "physicalmonitorenumerationapi": [],
+ "playsoundapi": [],
+ "portabledevice": [],
+ "portabledeviceapi": [],
+ "portabledevicetypes": [],
+ "powerbase": [],
+ "powersetting": [],
+ "powrprof": [],
+ "processenv": [],
+ "processsnapshot": [],
+ "processthreadsapi": [],
+ "processtopologyapi": [],
+ "profileapi": [],
+ "propidl": [],
+ "propkey": [],
+ "propkeydef": [],
+ "propsys": [],
+ "prsht": [],
+ "psapi": [],
+ "qos": [],
+ "realtimeapiset": [],
+ "reason": [],
+ "restartmanager": [],
+ "restrictederrorinfo": [],
+ "rmxfguid": [],
+ "roapi": [],
+ "robuffer": [],
+ "roerrorapi": [],
+ "rpc": [],
+ "rpcdce": [],
+ "rpcndr": [],
+ "rtinfo": [],
+ "sapi": [],
+ "sapi51": [],
+ "sapi53": [],
+ "sapiddk": [],
+ "sapiddk51": [],
+ "schannel": [],
+ "sddl": [],
+ "securityappcontainer": [],
+ "securitybaseapi": [],
+ "servprov": [],
+ "setupapi": [],
+ "shellapi": [],
+ "shellscalingapi": [],
+ "shlobj": [],
+ "shobjidl": [],
+ "shobjidl_core": [],
+ "shtypes": [],
+ "softpub": [],
+ "spapidef": [],
+ "spellcheck": [],
+ "sporder": [],
+ "sql": [],
+ "sqlext": [],
+ "sqltypes": [],
+ "sqlucode": [],
+ "sspi": [],
+ "std": [],
+ "stralign": [],
+ "stringapiset": [],
+ "strmif": [],
+ "subauth": [],
+ "synchapi": [],
+ "sysinfoapi": [],
+ "systemtopologyapi": [],
+ "taskschd": [],
+ "tcpestats": [],
+ "tcpmib": [],
+ "textstor": [],
+ "threadpoolapiset": [],
+ "threadpoollegacyapiset": [],
+ "timeapi": [],
+ "timezoneapi": [],
+ "tlhelp32": [],
+ "transportsettingcommon": [],
+ "tvout": [],
+ "udpmib": [],
+ "unknwnbase": [],
+ "urlhist": [],
+ "urlmon": [],
+ "usb": [],
+ "usbioctl": [],
+ "usbiodef": [],
+ "usbscan": [],
+ "usbspec": [],
+ "userenv": [],
+ "usp10": [],
+ "utilapiset": [],
+ "uxtheme": [],
+ "vadefs": [],
+ "vcruntime": [],
+ "vsbackup": [],
+ "vss": [],
+ "vsserror": [],
+ "vswriter": [],
+ "wbemads": [],
+ "wbemcli": [],
+ "wbemdisp": [],
+ "wbemprov": [],
+ "wbemtran": [],
+ "wct": [],
+ "werapi": [],
+ "winbase": [],
+ "wincodec": [],
+ "wincodecsdk": [],
+ "wincon": [],
+ "wincontypes": [],
+ "wincred": [],
+ "wincrypt": [],
+ "windef": [],
+ "windot11": [],
+ "windowsceip": [],
+ "windowsx": [],
+ "winefs": [],
+ "winerror": [],
+ "winevt": [],
+ "wingdi": [],
+ "winhttp": [],
+ "wininet": [],
+ "winineti": [],
+ "winioctl": [],
+ "winnetwk": [],
+ "winnls": [],
+ "winnt": [],
+ "winreg": [],
+ "winsafer": [],
+ "winscard": [],
+ "winsmcrd": [],
+ "winsock2": [],
+ "winspool": [],
+ "winstring": [],
+ "winsvc": [],
+ "wintrust": [],
+ "winusb": [],
+ "winusbio": [],
+ "winuser": [],
+ "winver": [],
+ "wlanapi": [],
+ "wlanihv": [],
+ "wlanihvtypes": [],
+ "wlantypes": [],
+ "wlclient": [],
+ "wmistr": [],
+ "wnnc": [],
+ "wow64apiset": [],
+ "wpdmtpextensions": [],
+ "ws2bth": [],
+ "ws2def": [],
+ "ws2ipdef": [],
+ "ws2spi": [],
+ "ws2tcpip": [],
+ "wtsapi32": [],
+ "wtypes": [],
+ "wtypesbase": [],
+ "xinput": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-pc-windows-msvc",
+ "features": [
+ "everything",
+ "impl-debug",
+ "impl-default"
+ ],
+ "targets": [
+ "aarch64-pc-windows-msvc",
+ "i686-pc-windows-msvc",
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os::windows-apis"
+ ],
+ "keywords": [
+ "windows",
+ "ffi",
+ "win32",
+ "com",
+ "directx"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/winapi/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-i686-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "strsim",
+ "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "wasi",
+ "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(target_os = \"wasi\")"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "rand",
+ "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "rand_core",
+ "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "getrandom",
+ "small_rng"
+ ]
+ },
+ {
+ "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "getrandom",
+ "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "getrandom"
+ ]
+ },
+ {
+ "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quickcheck",
+ "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "rand",
+ "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "aho-corasick",
+ "default",
+ "memchr",
+ "perf",
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "docopt",
+ "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memmap",
+ "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pkg_config",
+ "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+ "dependencies": [
+ "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "docopt",
+ "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "derive",
+ "serde_derive",
+ "std"
+ ]
+ },
+ {
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "syn",
+ "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "clone-impls",
+ "default",
+ "derive",
+ "parsing",
+ "printing",
+ "proc-macro",
+ "quote"
+ ]
+ },
+ {
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_i686_pc_windows_gnu",
+ "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "i686-pc-windows-gnu"
+ }
+ ]
+ },
+ {
+ "name": "winapi_x86_64_pc_windows_gnu",
+ "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "x86_64-pc-windows-gnu"
+ }
+ ]
+ }
+ ],
+ "features": [
+ "basetsd",
+ "handleapi",
+ "memoryapi",
+ "minwindef",
+ "std",
+ "sysinfoapi"
+ ]
+ },
+ {
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": "regex 1.7.1 (path+file:///$ROOT$regex)"
+ },
+ "target_directory": "$ROOT$regex/target",
+ "version": 1,
+ "workspace_root": "$ROOT$regex",
+ "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
new file mode 100644
index 000000000..131ff5dd7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
@@ -0,0 +1,12816 @@
+{
+ "packages": [
+ {
+ "name": "aho-corasick",
+ "version": "0.7.20",
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": [
+ "memchr/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "aho",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "aho-corasick",
+ "version": "1.0.1",
+ "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.17",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "perf-literal"
+ ],
+ "logging": [
+ "dep:log"
+ ],
+ "perf-literal": [
+ "dep:memchr"
+ ],
+ "std": [
+ "memchr?/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "pattern",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "atty",
+ "version": "0.2.14",
+ "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "A simple interface for querying atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "hermit-abi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(target_os = \"hermit\")",
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ },
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "consoleapi",
+ "processenv",
+ "minwinbase",
+ "minwindef",
+ "winbase"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "atty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "softprops <d.tangren@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "terminal",
+ "tty",
+ "isatty"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/softprops/atty",
+ "homepage": "https://github.com/softprops/atty",
+ "documentation": "http://softprops.github.io/atty",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "base64",
+ "version": "0.20.0",
+ "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "encodes and decodes base64 as bytes or utf8",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.5",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rstest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.12.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rstest_reuse",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "structopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.26",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "base64",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "base64",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "encode",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "tests",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benchmarks",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alice Maz <alice@alicemaz.com>",
+ "Marshall Pierce <marshall@mpierce.org>"
+ ],
+ "categories": [
+ "encoding"
+ ],
+ "keywords": [
+ "base64",
+ "utf8",
+ "encode",
+ "decode",
+ "no_std"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/marshallpierce/rust-base64",
+ "homepage": null,
+ "documentation": "https://docs.rs/base64",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.57.0"
+ },
+ {
+ "name": "bitflags",
+ "version": "1.3.2",
+ "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to generate structures which behave like bitflags.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bitflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "basic",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compile",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "example_generated": [],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "example_generated"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "bit",
+ "bitmask",
+ "bitflags",
+ "flags"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/bitflags/bitflags",
+ "homepage": "https://github.com/bitflags/bitflags",
+ "documentation": "https://docs.rs/bitflags",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "bstr",
+ "version": "1.4.0",
+ "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A string type that is not required to be valid UTF-8.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "once_cell",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.14.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-automata",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.85",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-parse",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-segmentation",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bstr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "graphemes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lines",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "uppercase",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "words",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "graphemes-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lines-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "uppercase-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "words-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [
+ "serde?/alloc"
+ ],
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "std": [
+ "alloc",
+ "memchr/std",
+ "serde?/std"
+ ],
+ "unicode": [
+ "dep:once_cell",
+ "dep:regex-automata"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding"
+ ],
+ "keywords": [
+ "string",
+ "str",
+ "byte",
+ "bytes",
+ "text"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/bstr",
+ "homepage": "https://github.com/BurntSushi/bstr",
+ "documentation": "https://docs.rs/bstr",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60"
+ },
+ {
+ "name": "bytecount",
+ "version": "0.6.3",
+ "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0/MIT",
+ "license_file": null,
+ "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.8",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bytecount",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "check",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "generic-simd": [
+ "packed_simd"
+ ],
+ "html_report": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "runtime-dispatch-simd": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andre Bogus <bogusandre@gmail.de>",
+ "Joshua Landau <joshua@landau.ws>"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/llogiq/bytecount",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cc",
+ "version": "1.0.79",
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jobserver",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gcc-shim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cc_env",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cxxflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "jobserver": [
+ "dep:jobserver"
+ ],
+ "parallel": [
+ "jobserver"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::build-utils"
+ ],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/cc-rs",
+ "homepage": "https://github.com/rust-lang/cc-rs",
+ "documentation": "https://docs.rs/cc",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "1.0.0",
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "clap",
+ "version": "2.34.0",
+ "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bitflags",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clippy",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "~0.0.166",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "strsim",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "term_size",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "textwrap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-width",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "vec_map",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "yaml-rust",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "version-sync",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ansi_term",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.12",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(not(windows))",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "clap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "ansi_term": [
+ "dep:ansi_term"
+ ],
+ "atty": [
+ "dep:atty"
+ ],
+ "clippy": [
+ "dep:clippy"
+ ],
+ "color": [
+ "ansi_term",
+ "atty"
+ ],
+ "debug": [],
+ "default": [
+ "suggestions",
+ "color",
+ "vec_map"
+ ],
+ "doc": [
+ "yaml"
+ ],
+ "nightly": [],
+ "no_cargo": [],
+ "strsim": [
+ "dep:strsim"
+ ],
+ "suggestions": [
+ "strsim"
+ ],
+ "term_size": [
+ "dep:term_size"
+ ],
+ "unstable": [],
+ "vec_map": [
+ "dep:vec_map"
+ ],
+ "wrap_help": [
+ "term_size",
+ "textwrap/term_size"
+ ],
+ "yaml": [
+ "yaml-rust"
+ ],
+ "yaml-rust": [
+ "dep:yaml-rust"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "doc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Kevin K. <kbknapp@gmail.com>"
+ ],
+ "categories": [
+ "command-line-interface"
+ ],
+ "keywords": [
+ "argument",
+ "cli",
+ "arg",
+ "parser",
+ "parse"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/clap-rs/clap",
+ "homepage": "https://clap.rs/",
+ "documentation": "https://docs.rs/clap/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "crossbeam-channel",
+ "version": "0.5.8",
+ "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Multi-producer multi-consumer channels for message passing",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-utils",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.13.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "signal-hook",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "crossbeam-channel",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "fibonacci",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "matching",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "stopwatch",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "after",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "array",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "golang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "iter",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "list",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "mpsc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "never",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "ready",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "same_channel",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "select",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "select_macro",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread_locals",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "tick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zero",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crossbeam",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "crossbeam-utils": [
+ "dep:crossbeam-utils"
+ ],
+ "default": [
+ "std"
+ ],
+ "std": [
+ "crossbeam-utils/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [
+ "algorithms",
+ "concurrency",
+ "data-structures"
+ ],
+ "keywords": [
+ "channel",
+ "mpmc",
+ "select",
+ "golang",
+ "message"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/crossbeam-rs/crossbeam",
+ "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.38"
+ },
+ {
+ "name": "crossbeam-utils",
+ "version": "0.8.15",
+ "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Utilities for concurrent programming",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "loom",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(crossbeam_loom)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "crossbeam-utils",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atomic_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cache_padded",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "parker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sharded_lock",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "wait_group",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atomic_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "loom": [
+ "dep:loom"
+ ],
+ "nightly": [],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [
+ "algorithms",
+ "concurrency",
+ "data-structures",
+ "no-std"
+ ],
+ "keywords": [
+ "scoped",
+ "thread",
+ "atomic",
+ "cache"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/crossbeam-rs/crossbeam",
+ "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.38"
+ },
+ {
+ "name": "encoding_rs",
+ "version": "0.8.32",
+ "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause",
+ "license_file": null,
+ "description": "A Gecko-oriented implementation of the Encoding Standard",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.4",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bincode",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "encoding_rs",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "alloc"
+ ],
+ "fast-big5-hanzi-encode": [],
+ "fast-gb-hanzi-encode": [],
+ "fast-hangul-encode": [],
+ "fast-hanja-encode": [],
+ "fast-kanji-encode": [],
+ "fast-legacy-encode": [
+ "fast-hangul-encode",
+ "fast-hanja-encode",
+ "fast-kanji-encode",
+ "fast-gb-hanzi-encode",
+ "fast-big5-hanzi-encode"
+ ],
+ "less-slow-big5-hanzi-encode": [],
+ "less-slow-gb-hanzi-encode": [],
+ "less-slow-kanji-encode": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "simd-accel": [
+ "packed_simd",
+ "packed_simd/into_bits"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Henri Sivonen <hsivonen@hsivonen.fi>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding",
+ "web-programming",
+ "internationalization"
+ ],
+ "keywords": [
+ "encoding",
+ "web",
+ "unicode",
+ "charset"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/hsivonen/encoding_rs",
+ "homepage": "https://docs.rs/encoding_rs/",
+ "documentation": "https://docs.rs/encoding_rs/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "encoding_rs_io",
+ "version": "0.1.7",
+ "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Streaming transcoding for encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "encoding_rs_io",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding",
+ "web-programming",
+ "email"
+ ],
+ "keywords": [
+ "encoding",
+ "transcoding",
+ "stream",
+ "io",
+ "read"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/encoding_rs_io",
+ "homepage": null,
+ "documentation": "https://docs.rs/encoding_rs_io",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "fnv",
+ "version": "1.0.7",
+ "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 / MIT",
+ "license_file": null,
+ "description": "Fowler–Noll–Vo hash function",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "fnv",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/servo/rust-fnv",
+ "homepage": null,
+ "documentation": "https://doc.servo.org/fnv/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "glob",
+ "version": "0.3.1",
+ "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Support for matching file paths against Unix shell style patterns.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "glob",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "glob-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "filesystem"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/glob",
+ "homepage": "https://github.com/rust-lang/glob",
+ "documentation": "https://docs.rs/glob/0.3.1",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "globset",
+ "version": "0.4.10",
+ "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fnv",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "perf",
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.104",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "glob",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.45",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "globset",
+ "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "log"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde"
+ ],
+ "simd-accel": []
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "glob",
+ "multiple",
+ "set",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+ "documentation": "https://docs.rs/globset",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep",
+ "version": "0.2.11",
+ "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast line oriented regex searching as a library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "grep-cli",
+ "source": null,
+ "req": "^0.1.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/cli"
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "grep-pcre2",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/pcre2"
+ },
+ {
+ "name": "grep-printer",
+ "source": null,
+ "req": "^0.1.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/printer"
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ },
+ {
+ "name": "grep-searcher",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/searcher"
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.2.7",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep",
+ "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "simplegrep",
+ "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "avx-accel": [],
+ "grep-pcre2": [
+ "dep:grep-pcre2"
+ ],
+ "pcre2": [
+ "grep-pcre2"
+ ],
+ "simd-accel": [
+ "grep-searcher/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+ "documentation": "https://docs.rs/grep",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-cli",
+ "version": "0.1.7",
+ "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Utilities for search oriented command line applications.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "globset",
+ "source": null,
+ "req": "^0.4.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/globset"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-cli",
+ "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "cli",
+ "utility",
+ "util"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+ "documentation": "https://docs.rs/grep-cli",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-matcher",
+ "version": "0.1.6",
+ "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A trait for regular expressions, with a focus on line oriented search.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-matcher",
+ "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "integration",
+ "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "pattern",
+ "trait"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+ "documentation": "https://docs.rs/grep-matcher",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-pcre2",
+ "version": "0.1.6",
+ "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Use PCRE2 with the 'grep' crate.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "pcre2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-pcre2",
+ "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "pcre",
+ "backreference",
+ "look"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+ "documentation": "https://docs.rs/grep-pcre2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-printer",
+ "version": "0.1.7",
+ "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "base64",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.20.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "grep-searcher",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/searcher"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.27",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-printer",
+ "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "base64": [
+ "dep:base64"
+ ],
+ "default": [
+ "serde1"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "base64",
+ "serde",
+ "serde_json"
+ ],
+ "serde_json": [
+ "dep:serde_json"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "grep",
+ "pattern",
+ "print",
+ "printer",
+ "sink"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+ "documentation": "https://docs.rs/grep-printer",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-regex",
+ "version": "0.1.11",
+ "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Use Rust's regex library with the 'grep' crate.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-regex",
+ "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "search",
+ "pattern",
+ "line"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+ "documentation": "https://docs.rs/grep-regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-searcher",
+ "version": "0.1.11",
+ "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast line oriented regex searching as a library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bytecount",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.14",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "encoding_rs_io",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memmap2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.3",
+ "kind": null,
+ "rename": "memmap",
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-searcher",
+ "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "search-stdin",
+ "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "avx-accel": [],
+ "default": [
+ "bytecount/runtime-dispatch-simd"
+ ],
+ "simd-accel": [
+ "encoding_rs/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+ "documentation": "https://docs.rs/grep-searcher",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "hermit-abi",
+ "version": "0.1.19",
+ "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.51",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "hermit-abi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "docs": [],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins/rustc-dep-of-std",
+ "libc/rustc-dep-of-std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-unknown-hermit",
+ "features": [
+ "docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Stefan Lankes"
+ ],
+ "categories": [
+ "os"
+ ],
+ "keywords": [
+ "unikernel",
+ "libos"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/hermitcore/libhermit-rs",
+ "homepage": null,
+ "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "ignore",
+ "version": "0.4.20",
+ "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "globset",
+ "source": null,
+ "req": "^0.4.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/globset"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.2.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-channel",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "ignore",
+ "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "walk",
+ "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gitignore_matched_path_or_any_parents_tests",
+ "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "simd-accel": [
+ "globset/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "glob",
+ "ignore",
+ "gitignore",
+ "pattern",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+ "documentation": "https://docs.rs/ignore",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "itoa",
+ "version": "1.0.6",
+ "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Fast integer primitive to string conversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "no-panic",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "itoa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "no-panic": [
+ "dep:no-panic"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "value-formatting",
+ "no-std"
+ ],
+ "keywords": [
+ "integer"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/itoa",
+ "homepage": null,
+ "documentation": "https://docs.rs/itoa",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "jemalloc-sys",
+ "version": "0.5.3+5.3.0-patched",
+ "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Rust FFI bindings to jemalloc\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.13",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jemalloc-sys",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloc_conf_empty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloc_conf_set",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "unprefixed_malloc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "background_threads": [
+ "background_threads_runtime_support"
+ ],
+ "background_threads_runtime_support": [],
+ "debug": [],
+ "default": [
+ "background_threads_runtime_support"
+ ],
+ "disable_initial_exec_tls": [],
+ "profiling": [],
+ "stats": [],
+ "unprefixed_malloc_on_supported_platforms": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustdoc-args": [
+ "--cfg",
+ "jemallocator_docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>",
+ "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
+ "The TiKV Project Developers"
+ ],
+ "categories": [],
+ "keywords": [
+ "allocator",
+ "jemalloc"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/tikv/jemallocator",
+ "homepage": "https://github.com/tikv/jemallocator",
+ "documentation": "https://docs.rs/jemallocator-sys",
+ "edition": "2018",
+ "links": "jemalloc",
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "jemallocator",
+ "version": "0.5.0",
+ "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A Rust allocator backed by jemalloc\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jemalloc-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "paste",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jemallocator",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "background_thread_defaults",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "background_thread_enabled",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "ffi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "grow_in_place",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloctl",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shrink_in_place",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "smoke",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "smoke_ffi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "usable_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "roundtrip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc_trait": [],
+ "background_threads": [
+ "jemalloc-sys/background_threads"
+ ],
+ "background_threads_runtime_support": [
+ "jemalloc-sys/background_threads_runtime_support"
+ ],
+ "debug": [
+ "jemalloc-sys/debug"
+ ],
+ "default": [
+ "background_threads_runtime_support"
+ ],
+ "disable_initial_exec_tls": [
+ "jemalloc-sys/disable_initial_exec_tls"
+ ],
+ "profiling": [
+ "jemalloc-sys/profiling"
+ ],
+ "stats": [
+ "jemalloc-sys/stats"
+ ],
+ "unprefixed_malloc_on_supported_platforms": [
+ "jemalloc-sys/unprefixed_malloc_on_supported_platforms"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [],
+ "rustdoc-args": [
+ "--cfg",
+ "jemallocator_docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>",
+ "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
+ "Simon Sapin <simon.sapin@exyr.org>",
+ "Steven Fackler <sfackler@gmail.com>",
+ "The TiKV Project Developers"
+ ],
+ "categories": [
+ "memory-management",
+ "api-bindings"
+ ],
+ "keywords": [
+ "allocator",
+ "jemalloc"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/tikv/jemallocator",
+ "homepage": "https://github.com/tikv/jemallocator",
+ "documentation": "https://docs.rs/jemallocator",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "jobserver",
+ "version": "0.1.26",
+ "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of the GNU make jobserver for Rust\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "futures",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tokio-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tokio-process",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.50",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jobserver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "client",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "server",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "client-of-myself",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "make-as-a-client",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "helper",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/jobserver-rs",
+ "homepage": "https://github.com/alexcrichton/jobserver-rs",
+ "documentation": "https://docs.rs/jobserver",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "lazy_static",
+ "version": "1.4.0",
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro for declaring lazily evaluated statics in Rust.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "spin",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "no_std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "spin": [
+ "dep:spin"
+ ],
+ "spin_no_std": [
+ "spin"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Marvin Löbel <loebel.marvin@gmail.com>"
+ ],
+ "categories": [
+ "no-std",
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "macro",
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/lazy_static",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "libc",
+ "version": "0.2.142",
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings to platform libraries like libc.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "libc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "const_fn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "align": [],
+ "const-extern-fn": [],
+ "default": [
+ "std"
+ ],
+ "extra_traits": [],
+ "rustc-dep-of-std": [
+ "align",
+ "rustc-std-workspace-core"
+ ],
+ "rustc-std-workspace-core": [
+ "dep:rustc-std-workspace-core"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "const-extern-fn",
+ "extra_traits"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os"
+ ],
+ "keywords": [
+ "libc",
+ "ffi",
+ "bindings",
+ "operating",
+ "system"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/libc",
+ "homepage": "https://github.com/rust-lang/libc",
+ "documentation": "https://docs.rs/libc/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "log",
+ "version": "0.4.17",
+ "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A lightweight logging facade for Rust\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "sval",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "value-bag",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.9",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_test",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "sval",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.5",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "value-bag",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.9",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "test"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "log",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "filters",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "macros",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "value",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "kv_unstable": [
+ "value-bag"
+ ],
+ "kv_unstable_serde": [
+ "kv_unstable_std",
+ "value-bag/serde",
+ "serde"
+ ],
+ "kv_unstable_std": [
+ "std",
+ "kv_unstable",
+ "value-bag/error"
+ ],
+ "kv_unstable_sval": [
+ "kv_unstable",
+ "value-bag/sval",
+ "sval"
+ ],
+ "max_level_debug": [],
+ "max_level_error": [],
+ "max_level_info": [],
+ "max_level_off": [],
+ "max_level_trace": [],
+ "max_level_warn": [],
+ "release_max_level_debug": [],
+ "release_max_level_error": [],
+ "release_max_level_info": [],
+ "release_max_level_off": [],
+ "release_max_level_trace": [],
+ "release_max_level_warn": [],
+ "serde": [
+ "dep:serde"
+ ],
+ "std": [],
+ "sval": [
+ "dep:sval"
+ ],
+ "value-bag": [
+ "dep:value-bag"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "std",
+ "serde",
+ "kv_unstable_std",
+ "kv_unstable_sval",
+ "kv_unstable_serde"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "development-tools::debugging"
+ ],
+ "keywords": [
+ "logging"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/log",
+ "homepage": null,
+ "documentation": "https://docs.rs/log",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memchr",
+ "version": "2.5.0",
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Safe interface to memchr.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memchr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>",
+ "bluss"
+ ],
+ "categories": [],
+ "keywords": [
+ "memchr",
+ "char",
+ "scan",
+ "strchr",
+ "string"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/memchr",
+ "homepage": "https://github.com/BurntSushi/memchr",
+ "documentation": "https://docs.rs/memchr/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memmap2",
+ "version": "0.5.10",
+ "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Cross-platform Rust API for memory-mapped file IO",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "stable_deref_trait",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "owning_ref",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memmap2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "stable_deref_trait": [
+ "dep:stable_deref_trait"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Dan Burkert <dan@danburkert.com>",
+ "Yevhenii Reizner <razrfalcon@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "mmap",
+ "memory-map",
+ "io",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/RazrFalcon/memmap2-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/memmap2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "once_cell",
+ "version": "1.17.1",
+ "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Single assignment cells and lazy values.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "atomic-polyfill",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": "atomic_polyfill",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "critical-section",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": "critical_section",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "parking_lot_core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.9.3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "critical-section",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.1",
+ "kind": "dev",
+ "rename": "critical_section",
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-utils",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.7",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "once_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench_acquire",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench_vs_lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reentrant_init_deadlocks",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_synchronization",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "it",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [
+ "race"
+ ],
+ "atomic-polyfill": [
+ "critical-section"
+ ],
+ "atomic_polyfill": [
+ "dep:atomic_polyfill"
+ ],
+ "critical-section": [
+ "critical_section",
+ "atomic_polyfill"
+ ],
+ "critical_section": [
+ "dep:critical_section"
+ ],
+ "default": [
+ "std"
+ ],
+ "parking_lot": [
+ "parking_lot_core"
+ ],
+ "parking_lot_core": [
+ "dep:parking_lot_core"
+ ],
+ "race": [],
+ "std": [
+ "alloc"
+ ],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Aleksey Kladov <aleksey.kladov@gmail.com>"
+ ],
+ "categories": [
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/matklad/once_cell",
+ "homepage": null,
+ "documentation": "https://docs.rs/once_cell",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "pcre2",
+ "version": "0.2.3",
+ "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "High level wrapper library for PCRE2.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.46",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pcre2-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pcre2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "pcre",
+ "pcre2",
+ "regex",
+ "jit",
+ "perl"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/rust-pcre2",
+ "homepage": "https://github.com/BurntSushi/rust-pcre2",
+ "documentation": "https://docs.rs/pcre2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pcre2-sys",
+ "version": "0.2.5",
+ "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Low level bindings to PCRE2.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "parallel"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pkg-config",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.13",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pcre2-sys",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings"
+ ],
+ "keywords": [
+ "pcre",
+ "pcre2",
+ "regex",
+ "jit"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/rust-pcre2",
+ "homepage": "https://github.com/BurntSushi/rust-pcre2",
+ "documentation": "https://docs.rs/pcre2-sys",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pkg-config",
+ "version": "0.3.26",
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pkg-config",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/pkg-config-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/pkg-config",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "proc-macro2",
+ "version": "1.0.56",
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "proc-macro2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "comments",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "features",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "marker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_fmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "nightly": [],
+ "proc-macro": [],
+ "span-locations": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt",
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "span-locations"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>",
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/proc-macro2",
+ "homepage": null,
+ "documentation": "https://docs.rs/proc-macro2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "quote",
+ "version": "1.0.26",
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Quasi-quoting macro quote!(...)",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.52",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.66",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quote",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/quote",
+ "homepage": null,
+ "documentation": "https://docs.rs/quote/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "regex",
+ "version": "1.8.1",
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "regex-automata",
+ "version": "0.1.10",
+ "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Automata construction and matching using regular expressions.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.82",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_bytes",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.82",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "toml",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-automata",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "fst": [
+ "dep:fst"
+ ],
+ "regex-syntax": [
+ "dep:regex-syntax"
+ ],
+ "std": [
+ "regex-syntax"
+ ],
+ "transducer": [
+ "std",
+ "fst"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "regex",
+ "dfa",
+ "automata",
+ "automaton",
+ "nfa"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/regex-automata",
+ "homepage": "https://github.com/BurntSushi/regex-automata",
+ "documentation": "https://docs.rs/regex-automata",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.6.29",
+ "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "unicode"
+ ],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.7.1",
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "ripgrep",
+ "version": "13.0.0",
+ "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.33.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "suggestions"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep",
+ "source": null,
+ "req": "^0.2.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/grep"
+ },
+ {
+ "name": "ignore",
+ "source": null,
+ "req": "^0.4.19",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/ignore"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.23",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.33.0",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "suggestions"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "jemallocator",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rg",
+ "src_path": "$ROOT$ripgrep/crates/core/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "integration",
+ "src_path": "$ROOT$ripgrep/tests/tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$ripgrep/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "pcre2": [
+ "grep/pcre2"
+ ],
+ "simd-accel": [
+ "grep/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/Cargo.toml",
+ "metadata": {
+ "deb": {
+ "assets": [
+ [
+ "target/release/rg",
+ "usr/bin/",
+ "755"
+ ],
+ [
+ "COPYING",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "LICENSE-MIT",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "UNLICENSE",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "CHANGELOG.md",
+ "usr/share/doc/ripgrep/CHANGELOG",
+ "644"
+ ],
+ [
+ "README.md",
+ "usr/share/doc/ripgrep/README",
+ "644"
+ ],
+ [
+ "FAQ.md",
+ "usr/share/doc/ripgrep/FAQ",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.1",
+ "usr/share/man/man1/rg.1",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.bash",
+ "usr/share/bash-completion/completions/rg",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.fish",
+ "usr/share/fish/vendor_completions.d/rg.fish",
+ "644"
+ ],
+ [
+ "deployment/deb/_rg",
+ "usr/share/zsh/vendor-completions/",
+ "644"
+ ]
+ ],
+ "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n",
+ "features": [
+ "pcre2"
+ ],
+ "section": "utils"
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "command-line-utilities",
+ "text-processing"
+ ],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep",
+ "homepage": "https://github.com/BurntSushi/ripgrep",
+ "documentation": "https://github.com/BurntSushi/ripgrep",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.65"
+ },
+ {
+ "name": "ryu",
+ "version": "1.0.13",
+ "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 OR BSL-1.0",
+ "license_file": null,
+ "description": "Fast floating point to string conversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "no-panic",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_xorshift",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "ryu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "upstream_benchmark",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "common_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "d2s_table_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "d2s_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "exhaustive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "f2s_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "s2d_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "s2f_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "no-panic": [
+ "dep:no-panic"
+ ],
+ "small": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "value-formatting",
+ "no-std"
+ ],
+ "keywords": [
+ "float"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/ryu",
+ "homepage": null,
+ "documentation": "https://docs.rs/ryu",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "same-file",
+ "version": "1.0.6",
+ "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "A simple crate for determining whether two file paths point to the same file.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "same-file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "is_same_file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "is_stderr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "same",
+ "file",
+ "equal",
+ "inode"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/same-file",
+ "homepage": "https://github.com/BurntSushi/same-file",
+ "documentation": "https://docs.rs/same-file",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "serde",
+ "version": "1.0.160",
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A generic serialization/deserialization framework",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.160",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "derive": [
+ "serde_derive"
+ ],
+ "rc": [],
+ "serde_derive": [
+ "dep:serde_derive"
+ ],
+ "std": [],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "derive"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "derive",
+ "rc"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://docs.rs/serde",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.19"
+ },
+ {
+ "name": "serde_derive",
+ "version": "1.0.160",
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "proc-macro"
+ ],
+ "crate_types": [
+ "proc-macro"
+ ],
+ "name": "serde_derive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [],
+ "deserialize_in_place": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std",
+ "derive"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://serde.rs/derive.html",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "serde_json",
+ "version": "1.0.96",
+ "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A JSON serialization file format",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "indexmap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.5.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "itoa",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ryu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.100",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "indoc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.100",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_bytes",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_stacker",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.49",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde_json",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "debug",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lexical",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "map",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [
+ "serde/alloc"
+ ],
+ "arbitrary_precision": [],
+ "default": [
+ "std"
+ ],
+ "float_roundtrip": [],
+ "indexmap": [
+ "dep:indexmap"
+ ],
+ "preserve_order": [
+ "indexmap",
+ "std"
+ ],
+ "raw_value": [],
+ "std": [
+ "serde/std"
+ ],
+ "unbounded_depth": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "raw_value",
+ "unbounded_depth"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "raw_value"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "parser-implementations",
+ "no-std"
+ ],
+ "keywords": [
+ "json",
+ "serde",
+ "serialization"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/serde-rs/json",
+ "homepage": null,
+ "documentation": "https://docs.rs/serde_json",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "strsim",
+ "version": "0.8.0",
+ "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "strsim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lib",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benches",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Danny Guo <dannyguo91@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "string",
+ "similarity",
+ "Hamming",
+ "Levenshtein",
+ "Jaro"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dguo/strsim-rs",
+ "homepage": "https://github.com/dguo/strsim-rs",
+ "documentation": "https://docs.rs/strsim/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "syn",
+ "version": "2.0.15",
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Parser for Rust source code",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.55",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.25",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "anyhow",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "flate2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "insta",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rayon",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "reqwest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "blocking"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn-test-suite",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.16",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "syn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_asyncness",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_attribute",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_derive_input",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_expr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_generics",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_grouping",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_item",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_iterators",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_lit",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_meta",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_pat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_path",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_precedence",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_receiver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_round_trip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_shebang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_should_parse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_stmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_token_trees",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_visibility",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zzz_stable",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rust",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "clone-impls": [],
+ "default": [
+ "derive",
+ "parsing",
+ "printing",
+ "clone-impls",
+ "proc-macro"
+ ],
+ "derive": [],
+ "extra-traits": [],
+ "fold": [],
+ "full": [],
+ "parsing": [],
+ "printing": [
+ "quote"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro",
+ "quote/proc-macro"
+ ],
+ "quote": [
+ "dep:quote"
+ ],
+ "test": [
+ "syn-test-suite/all-features"
+ ],
+ "visit": [],
+ "visit-mut": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "full",
+ "visit",
+ "visit-mut",
+ "fold",
+ "extra-traits"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "parser-implementations"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/syn",
+ "homepage": null,
+ "documentation": "https://docs.rs/syn",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "termcolor",
+ "version": "1.2.0",
+ "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A simple cross platform library for writing colored text to a terminal.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "termcolor",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows",
+ "win",
+ "color",
+ "ansi",
+ "console"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/termcolor",
+ "homepage": "https://github.com/BurntSushi/termcolor",
+ "documentation": "https://docs.rs/termcolor",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "textwrap",
+ "version": "0.11.0",
+ "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "hyphenation",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "embed_all"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "term_size",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-width",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lipsum",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_xorshift",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "version-sync",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "textwrap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "layout",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "termwidth",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "version-numbers",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "linear",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "hyphenation": [
+ "dep:hyphenation"
+ ],
+ "term_size": [
+ "dep:term_size"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Martin Geisler <martin@geisler.net>"
+ ],
+ "categories": [
+ "text-processing",
+ "command-line-interface"
+ ],
+ "keywords": [
+ "text",
+ "formatting",
+ "wrap",
+ "typesetting",
+ "hyphenation"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/mgeisler/textwrap",
+ "homepage": null,
+ "documentation": "https://docs.rs/textwrap/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "thread_local",
+ "version": "1.1.7",
+ "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Per-object thread-local storage",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "once_cell",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.5.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "thread_local",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread_local",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "nightly": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Amanieu d'Antras <amanieu@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "thread_local",
+ "concurrent",
+ "thread"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/Amanieu/thread_local-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/thread_local/",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "unicode-ident",
+ "version": "1.0.8",
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+ "license_file": null,
+ "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "roaring",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-trie",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-xid",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compare",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "static_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xid",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "no-std"
+ ],
+ "keywords": [
+ "unicode",
+ "xid"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/unicode-ident",
+ "homepage": null,
+ "documentation": "https://docs.rs/unicode-ident",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "unicode-width",
+ "version": "0.1.10",
+ "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-std",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "std",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-width",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "bench": [],
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "no_std": [],
+ "rustc-dep-of-std": [
+ "std",
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [
+ "dep:std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "kwantam <kwantam@gmail.com>",
+ "Manish Goregaokar <manishsmail@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "text",
+ "width",
+ "unicode"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/unicode-rs/unicode-width",
+ "homepage": "https://github.com/unicode-rs/unicode-width",
+ "documentation": "https://unicode-rs.github.io/unicode-width",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "walkdir",
+ "version": "2.3.3",
+ "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Recursively walk a directory.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "walkdir",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "filesystem"
+ ],
+ "keywords": [
+ "directory",
+ "recursive",
+ "walk",
+ "iterator"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/walkdir",
+ "homepage": "https://github.com/BurntSushi/walkdir",
+ "documentation": "https://docs.rs/walkdir/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi",
+ "version": "0.3.9",
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings for all of Windows API.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "i686-pc-windows-gnu",
+ "registry": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "x86_64-pc-windows-gnu",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "accctrl": [],
+ "aclapi": [],
+ "activation": [],
+ "adhoc": [],
+ "appmgmt": [],
+ "audioclient": [],
+ "audiosessiontypes": [],
+ "avrt": [],
+ "basetsd": [],
+ "bcrypt": [],
+ "bits": [],
+ "bits10_1": [],
+ "bits1_5": [],
+ "bits2_0": [],
+ "bits2_5": [],
+ "bits3_0": [],
+ "bits4_0": [],
+ "bits5_0": [],
+ "bitscfg": [],
+ "bitsmsg": [],
+ "bluetoothapis": [],
+ "bluetoothleapis": [],
+ "bthdef": [],
+ "bthioctl": [],
+ "bthledef": [],
+ "bthsdpdef": [],
+ "bugcodes": [],
+ "cderr": [],
+ "cfg": [],
+ "cfgmgr32": [],
+ "cguid": [],
+ "combaseapi": [],
+ "coml2api": [],
+ "commapi": [],
+ "commctrl": [],
+ "commdlg": [],
+ "commoncontrols": [],
+ "consoleapi": [],
+ "corecrt": [],
+ "corsym": [],
+ "d2d1": [],
+ "d2d1_1": [],
+ "d2d1_2": [],
+ "d2d1_3": [],
+ "d2d1effectauthor": [],
+ "d2d1effects": [],
+ "d2d1effects_1": [],
+ "d2d1effects_2": [],
+ "d2d1svg": [],
+ "d2dbasetypes": [],
+ "d3d": [],
+ "d3d10": [],
+ "d3d10_1": [],
+ "d3d10_1shader": [],
+ "d3d10effect": [],
+ "d3d10misc": [],
+ "d3d10sdklayers": [],
+ "d3d10shader": [],
+ "d3d11": [],
+ "d3d11_1": [],
+ "d3d11_2": [],
+ "d3d11_3": [],
+ "d3d11_4": [],
+ "d3d11on12": [],
+ "d3d11sdklayers": [],
+ "d3d11shader": [],
+ "d3d11tokenizedprogramformat": [],
+ "d3d12": [],
+ "d3d12sdklayers": [],
+ "d3d12shader": [],
+ "d3d9": [],
+ "d3d9caps": [],
+ "d3d9types": [],
+ "d3dcommon": [],
+ "d3dcompiler": [],
+ "d3dcsx": [],
+ "d3dkmdt": [],
+ "d3dkmthk": [],
+ "d3dukmdt": [],
+ "d3dx10core": [],
+ "d3dx10math": [],
+ "d3dx10mesh": [],
+ "datetimeapi": [],
+ "davclnt": [],
+ "dbghelp": [],
+ "dbt": [],
+ "dcommon": [],
+ "dcomp": [],
+ "dcompanimation": [],
+ "dcomptypes": [],
+ "dde": [],
+ "ddraw": [],
+ "ddrawi": [],
+ "ddrawint": [],
+ "debug": [
+ "impl-debug"
+ ],
+ "debugapi": [],
+ "devguid": [],
+ "devicetopology": [],
+ "devpkey": [],
+ "devpropdef": [],
+ "dinput": [],
+ "dinputd": [],
+ "dispex": [],
+ "dmksctl": [],
+ "dmusicc": [],
+ "docobj": [],
+ "documenttarget": [],
+ "dot1x": [],
+ "dpa_dsa": [],
+ "dpapi": [],
+ "dsgetdc": [],
+ "dsound": [],
+ "dsrole": [],
+ "dvp": [],
+ "dwmapi": [],
+ "dwrite": [],
+ "dwrite_1": [],
+ "dwrite_2": [],
+ "dwrite_3": [],
+ "dxdiag": [],
+ "dxfile": [],
+ "dxgi": [],
+ "dxgi1_2": [],
+ "dxgi1_3": [],
+ "dxgi1_4": [],
+ "dxgi1_5": [],
+ "dxgi1_6": [],
+ "dxgidebug": [],
+ "dxgiformat": [],
+ "dxgitype": [],
+ "dxva2api": [],
+ "dxvahd": [],
+ "eaptypes": [],
+ "enclaveapi": [],
+ "endpointvolume": [],
+ "errhandlingapi": [],
+ "everything": [],
+ "evntcons": [],
+ "evntprov": [],
+ "evntrace": [],
+ "excpt": [],
+ "exdisp": [],
+ "fibersapi": [],
+ "fileapi": [],
+ "functiondiscoverykeys_devpkey": [],
+ "gl-gl": [],
+ "guiddef": [],
+ "handleapi": [],
+ "heapapi": [],
+ "hidclass": [],
+ "hidpi": [],
+ "hidsdi": [],
+ "hidusage": [],
+ "highlevelmonitorconfigurationapi": [],
+ "hstring": [],
+ "http": [],
+ "ifdef": [],
+ "ifmib": [],
+ "imm": [],
+ "impl-debug": [],
+ "impl-default": [],
+ "in6addr": [],
+ "inaddr": [],
+ "inspectable": [],
+ "interlockedapi": [],
+ "intsafe": [],
+ "ioapiset": [],
+ "ipexport": [],
+ "iphlpapi": [],
+ "ipifcons": [],
+ "ipmib": [],
+ "iprtrmib": [],
+ "iptypes": [],
+ "jobapi": [],
+ "jobapi2": [],
+ "knownfolders": [],
+ "ks": [],
+ "ksmedia": [],
+ "ktmtypes": [],
+ "ktmw32": [],
+ "l2cmn": [],
+ "libloaderapi": [],
+ "limits": [],
+ "lmaccess": [],
+ "lmalert": [],
+ "lmapibuf": [],
+ "lmat": [],
+ "lmcons": [],
+ "lmdfs": [],
+ "lmerrlog": [],
+ "lmjoin": [],
+ "lmmsg": [],
+ "lmremutl": [],
+ "lmrepl": [],
+ "lmserver": [],
+ "lmshare": [],
+ "lmstats": [],
+ "lmsvc": [],
+ "lmuse": [],
+ "lmwksta": [],
+ "lowlevelmonitorconfigurationapi": [],
+ "lsalookup": [],
+ "memoryapi": [],
+ "minschannel": [],
+ "minwinbase": [],
+ "minwindef": [],
+ "mmdeviceapi": [],
+ "mmeapi": [],
+ "mmreg": [],
+ "mmsystem": [],
+ "mprapidef": [],
+ "msaatext": [],
+ "mscat": [],
+ "mschapp": [],
+ "mssip": [],
+ "mstcpip": [],
+ "mswsock": [],
+ "mswsockdef": [],
+ "namedpipeapi": [],
+ "namespaceapi": [],
+ "nb30": [],
+ "ncrypt": [],
+ "netioapi": [],
+ "nldef": [],
+ "ntddndis": [],
+ "ntddscsi": [],
+ "ntddser": [],
+ "ntdef": [],
+ "ntlsa": [],
+ "ntsecapi": [],
+ "ntstatus": [],
+ "oaidl": [],
+ "objbase": [],
+ "objidl": [],
+ "objidlbase": [],
+ "ocidl": [],
+ "ole2": [],
+ "oleauto": [],
+ "olectl": [],
+ "oleidl": [],
+ "opmapi": [],
+ "pdh": [],
+ "perflib": [],
+ "physicalmonitorenumerationapi": [],
+ "playsoundapi": [],
+ "portabledevice": [],
+ "portabledeviceapi": [],
+ "portabledevicetypes": [],
+ "powerbase": [],
+ "powersetting": [],
+ "powrprof": [],
+ "processenv": [],
+ "processsnapshot": [],
+ "processthreadsapi": [],
+ "processtopologyapi": [],
+ "profileapi": [],
+ "propidl": [],
+ "propkey": [],
+ "propkeydef": [],
+ "propsys": [],
+ "prsht": [],
+ "psapi": [],
+ "qos": [],
+ "realtimeapiset": [],
+ "reason": [],
+ "restartmanager": [],
+ "restrictederrorinfo": [],
+ "rmxfguid": [],
+ "roapi": [],
+ "robuffer": [],
+ "roerrorapi": [],
+ "rpc": [],
+ "rpcdce": [],
+ "rpcndr": [],
+ "rtinfo": [],
+ "sapi": [],
+ "sapi51": [],
+ "sapi53": [],
+ "sapiddk": [],
+ "sapiddk51": [],
+ "schannel": [],
+ "sddl": [],
+ "securityappcontainer": [],
+ "securitybaseapi": [],
+ "servprov": [],
+ "setupapi": [],
+ "shellapi": [],
+ "shellscalingapi": [],
+ "shlobj": [],
+ "shobjidl": [],
+ "shobjidl_core": [],
+ "shtypes": [],
+ "softpub": [],
+ "spapidef": [],
+ "spellcheck": [],
+ "sporder": [],
+ "sql": [],
+ "sqlext": [],
+ "sqltypes": [],
+ "sqlucode": [],
+ "sspi": [],
+ "std": [],
+ "stralign": [],
+ "stringapiset": [],
+ "strmif": [],
+ "subauth": [],
+ "synchapi": [],
+ "sysinfoapi": [],
+ "systemtopologyapi": [],
+ "taskschd": [],
+ "tcpestats": [],
+ "tcpmib": [],
+ "textstor": [],
+ "threadpoolapiset": [],
+ "threadpoollegacyapiset": [],
+ "timeapi": [],
+ "timezoneapi": [],
+ "tlhelp32": [],
+ "transportsettingcommon": [],
+ "tvout": [],
+ "udpmib": [],
+ "unknwnbase": [],
+ "urlhist": [],
+ "urlmon": [],
+ "usb": [],
+ "usbioctl": [],
+ "usbiodef": [],
+ "usbscan": [],
+ "usbspec": [],
+ "userenv": [],
+ "usp10": [],
+ "utilapiset": [],
+ "uxtheme": [],
+ "vadefs": [],
+ "vcruntime": [],
+ "vsbackup": [],
+ "vss": [],
+ "vsserror": [],
+ "vswriter": [],
+ "wbemads": [],
+ "wbemcli": [],
+ "wbemdisp": [],
+ "wbemprov": [],
+ "wbemtran": [],
+ "wct": [],
+ "werapi": [],
+ "winbase": [],
+ "wincodec": [],
+ "wincodecsdk": [],
+ "wincon": [],
+ "wincontypes": [],
+ "wincred": [],
+ "wincrypt": [],
+ "windef": [],
+ "windot11": [],
+ "windowsceip": [],
+ "windowsx": [],
+ "winefs": [],
+ "winerror": [],
+ "winevt": [],
+ "wingdi": [],
+ "winhttp": [],
+ "wininet": [],
+ "winineti": [],
+ "winioctl": [],
+ "winnetwk": [],
+ "winnls": [],
+ "winnt": [],
+ "winreg": [],
+ "winsafer": [],
+ "winscard": [],
+ "winsmcrd": [],
+ "winsock2": [],
+ "winspool": [],
+ "winstring": [],
+ "winsvc": [],
+ "wintrust": [],
+ "winusb": [],
+ "winusbio": [],
+ "winuser": [],
+ "winver": [],
+ "wlanapi": [],
+ "wlanihv": [],
+ "wlanihvtypes": [],
+ "wlantypes": [],
+ "wlclient": [],
+ "wmistr": [],
+ "wnnc": [],
+ "wow64apiset": [],
+ "wpdmtpextensions": [],
+ "ws2bth": [],
+ "ws2def": [],
+ "ws2ipdef": [],
+ "ws2spi": [],
+ "ws2tcpip": [],
+ "wtsapi32": [],
+ "wtypes": [],
+ "wtypesbase": [],
+ "xinput": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-pc-windows-msvc",
+ "features": [
+ "everything",
+ "impl-debug",
+ "impl-default"
+ ],
+ "targets": [
+ "aarch64-pc-windows-msvc",
+ "i686-pc-windows-msvc",
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os::windows-apis"
+ ],
+ "keywords": [
+ "windows",
+ "ffi",
+ "win32",
+ "com",
+ "directx"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/winapi/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-i686-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-util",
+ "version": "0.1.5",
+ "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "A dumping ground for high level safe wrappers over winapi.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "std",
+ "consoleapi",
+ "errhandlingapi",
+ "fileapi",
+ "minwindef",
+ "processenv",
+ "winbase",
+ "wincon",
+ "winerror",
+ "winnt"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-util",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "os::windows-apis",
+ "external-ffi-bindings"
+ ],
+ "keywords": [
+ "windows",
+ "winapi",
+ "util",
+ "win"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/winapi-util",
+ "homepage": "https://github.com/BurntSushi/winapi-util",
+ "documentation": "https://docs.rs/winapi-util",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "perf-literal",
+ "std"
+ ]
+ },
+ {
+ "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "hermit_abi",
+ "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(target_os = \"hermit\")"
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "once_cell",
+ "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_automata",
+ "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default",
+ "std",
+ "unicode"
+ ]
+ },
+ {
+ "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "runtime-dispatch-simd"
+ ]
+ },
+ {
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "jobserver",
+ "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "jobserver",
+ "parallel"
+ ]
+ },
+ {
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bitflags",
+ "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "strsim",
+ "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "textwrap",
+ "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_width",
+ "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "strsim",
+ "suggestions"
+ ]
+ },
+ {
+ "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "crossbeam_utils",
+ "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "crossbeam-utils",
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "std"
+ ]
+ },
+ {
+ "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default"
+ ]
+ },
+ {
+ "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "encoding_rs",
+ "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "fnv",
+ "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "glob",
+ "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "log"
+ ]
+ },
+ {
+ "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "dependencies": [
+ "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "grep_cli",
+ "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_printer",
+ "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_searcher",
+ "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "dependencies": [
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "atty",
+ "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "globset",
+ "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "dependencies": [
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pcre2",
+ "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "dependencies": [
+ "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "base64",
+ "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_searcher",
+ "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "base64",
+ "default",
+ "serde",
+ "serde1",
+ "serde_json"
+ ]
+ },
+ {
+ "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dependencies": [
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bytecount",
+ "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "encoding_rs",
+ "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "encoding_rs_io",
+ "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memmap",
+ "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "dependencies": [
+ "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "crossbeam_channel",
+ "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "globset",
+ "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "background_threads_runtime_support"
+ ]
+ },
+ {
+ "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "jemalloc_sys",
+ "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "background_threads_runtime_support",
+ "default"
+ ]
+ },
+ {
+ "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "alloc",
+ "default",
+ "race",
+ "std"
+ ]
+ },
+ {
+ "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pcre2_sys",
+ "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pkg_config",
+ "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "aho-corasick",
+ "default",
+ "memchr",
+ "perf",
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+ "dependencies": [
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "clap",
+ "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ },
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep",
+ "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "ignore",
+ "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "jemallocator",
+ "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))"
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ },
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default",
+ "derive",
+ "serde_derive",
+ "std"
+ ]
+ },
+ {
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "syn",
+ "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "itoa",
+ "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "ryu",
+ "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "clone-impls",
+ "default",
+ "derive",
+ "parsing",
+ "printing",
+ "proc-macro",
+ "quote"
+ ]
+ },
+ {
+ "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_width",
+ "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "once_cell",
+ "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_i686_pc_windows_gnu",
+ "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "i686-pc-windows-gnu"
+ }
+ ]
+ },
+ {
+ "name": "winapi_x86_64_pc_windows_gnu",
+ "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "x86_64-pc-windows-gnu"
+ }
+ ]
+ }
+ ],
+ "features": [
+ "consoleapi",
+ "errhandlingapi",
+ "fileapi",
+ "minwinbase",
+ "minwindef",
+ "processenv",
+ "std",
+ "winbase",
+ "wincon",
+ "winerror",
+ "winnt"
+ ]
+ },
+ {
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+ },
+ "target_directory": "$ROOT$ripgrep/target",
+ "version": 1,
+ "workspace_root": "$ROOT$ripgrep",
+ "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index f0f1900c7..5b72d5756 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -29,9 +29,8 @@ parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"
rustc-hash = "1.1.0"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = { version = "1.0.81", features = ["preserve_order"] }
-threadpool = "1.8.1"
+serde_json = { workspace = true, features = ["preserve_order"] }
+serde.workspace = true
rayon = "1.6.1"
num_cpus = "1.15.0"
mimalloc = { version = "0.1.30", default-features = false, optional = true }
@@ -45,8 +44,20 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features =
] }
tracing-log = "0.1.3"
tracing-tree = "0.2.1"
+triomphe.workspace = true
+nohash-hasher.workspace = true
always-assert = "0.1.2"
+# These dependencies are unused, but we pin them to a version here to restrict them for our transitive dependencies
+# so that we don't pull in duplicates of their dependencies like windows-sys and syn 1 vs 2
+# these would pull in serde 2
+thiserror = "=1.0.39"
+serde_repr = "=0.1.11"
+# these would pull in windows-sys 0.45.0
+mio = "=0.8.5"
+filetime = "=0.2.19"
+parking_lot_core = "=0.9.6"
+
cfg.workspace = true
flycheck.workspace = true
hir-def.workspace = true
@@ -57,7 +68,6 @@ ide-db.workspace = true
ide-ssr.workspace = true
ide.workspace = true
proc-macro-api.workspace = true
-proc-macro-srv.workspace = true
profile.workspace = true
project-model.workspace = true
stdx.workspace = true
@@ -75,7 +85,6 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr
[dev-dependencies]
expect-test = "1.4.0"
-jod-thread = "0.1.2"
xshell = "0.2.2"
test-utils.workspace = true
@@ -85,8 +94,5 @@ mbe.workspace = true
[features]
jemalloc = ["jemallocator", "profile/jemalloc"]
force-always-assert = ["always-assert/force"]
-in-rust-tree = [
- "proc-macro-srv/sysroot-abi",
- "ide/in-rust-tree",
- "syntax/in-rust-tree",
-]
+sysroot-abi = []
+in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 4de022b6e..91911dd18 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -7,7 +7,11 @@
mod logger;
mod rustc_wrapper;
-use std::{env, fs, path::Path, process};
+use std::{
+ env, fs,
+ path::{Path, PathBuf},
+ process,
+};
use lsp_server::Connection;
use rust_analyzer::{cli::flags, config::Config, from_json, Result};
@@ -74,10 +78,16 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
println!("rust-analyzer {}", rust_analyzer::version());
return Ok(());
}
- with_extra_thread("LspServer", run_server)?;
- }
- flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
- with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?;
+
+ // rust-analyzer’s “main thread” is actually
+ // a secondary latency-sensitive thread with an increased stack size.
+ // We use this thread intent because any delay in the main loop
+ // will make actions like hitting enter in the editor slow.
+ with_extra_thread(
+ "LspServer",
+ stdx::thread::ThreadIntent::LatencySensitive,
+ run_server,
+ )?;
}
flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
@@ -135,14 +145,17 @@ const STACK_SIZE: usize = 1024 * 1024 * 8;
/// space.
fn with_extra_thread(
thread_name: impl Into<String>,
+ thread_intent: stdx::thread::ThreadIntent,
f: impl FnOnce() -> Result<()> + Send + 'static,
) -> Result<()> {
- let handle =
- std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?;
- match handle.join() {
- Ok(res) => res,
- Err(panic) => std::panic::resume_unwind(panic),
- }
+ let handle = stdx::thread::Builder::new(thread_intent)
+ .name(thread_name.into())
+ .stack_size(STACK_SIZE)
+ .spawn(f)?;
+
+ handle.join()?;
+
+ Ok(())
}
fn run_server() -> Result<()> {
@@ -152,12 +165,18 @@ fn run_server() -> Result<()> {
let (initialize_id, initialize_params) = connection.initialize_start()?;
tracing::info!("InitializeParams: {}", initialize_params);
- let initialize_params =
- from_json::<lsp_types::InitializeParams>("InitializeParams", &initialize_params)?;
-
- let root_path = match initialize_params
- .root_uri
+ let lsp_types::InitializeParams {
+ root_uri,
+ capabilities,
+ workspace_folders,
+ initialization_options,
+ client_info,
+ ..
+ } = from_json::<lsp_types::InitializeParams>("InitializeParams", &initialize_params)?;
+
+ let root_path = match root_uri
.and_then(|it| it.to_file_path().ok())
+ .map(patch_path_prefix)
.and_then(|it| AbsPathBuf::try_from(it).ok())
{
Some(it) => it,
@@ -167,19 +186,19 @@ fn run_server() -> Result<()> {
}
};
- let workspace_roots = initialize_params
- .workspace_folders
+ let workspace_roots = workspace_folders
.map(|workspaces| {
workspaces
.into_iter()
.filter_map(|it| it.uri.to_file_path().ok())
+ .map(patch_path_prefix)
.filter_map(|it| AbsPathBuf::try_from(it).ok())
.collect::<Vec<_>>()
})
.filter(|workspaces| !workspaces.is_empty())
.unwrap_or_else(|| vec![root_path.clone()]);
- let mut config = Config::new(root_path, initialize_params.capabilities, workspace_roots);
- if let Some(json) = initialize_params.initialization_options {
+ let mut config = Config::new(root_path, capabilities, workspace_roots);
+ if let Some(json) = initialization_options {
if let Err(e) = config.update(json) {
use lsp_types::{
notification::{Notification, ShowMessage},
@@ -208,7 +227,7 @@ fn run_server() -> Result<()> {
connection.initialize_finish(initialize_id, initialize_result)?;
- if let Some(client_info) = initialize_params.client_info {
+ if let Some(client_info) = client_info {
tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
}
@@ -222,3 +241,42 @@ fn run_server() -> Result<()> {
tracing::info!("server did shut down");
Ok(())
}
+
+fn patch_path_prefix(path: PathBuf) -> PathBuf {
+ use std::path::{Component, Prefix};
+ if cfg!(windows) {
+ // VSCode might report paths with the file drive in lowercase, but this can mess
+ // with env vars set by tools and build scripts executed by r-a such that it invalidates
+ // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683
+ // So we just uppercase the drive letter here unconditionally.
+ // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows)
+ let mut comps = path.components();
+ match comps.next() {
+ Some(Component::Prefix(prefix)) => {
+ let prefix = match prefix.kind() {
+ Prefix::Disk(d) => {
+ format!("{}:", d.to_ascii_uppercase() as char)
+ }
+ Prefix::VerbatimDisk(d) => {
+ format!(r"\\?\{}:", d.to_ascii_uppercase() as char)
+ }
+ _ => return path,
+ };
+ let mut path = PathBuf::new();
+ path.push(prefix);
+ path.extend(comps);
+ path
+ }
+ _ => path,
+ }
+ } else {
+ path
+ }
+}
+
+#[test]
+#[cfg(windows)]
+fn patch_path_prefix_works() {
+ assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar"));
+ assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar"));
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index 3628670ac..ab06b9681 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -23,13 +23,14 @@ use crate::semantic_tokens;
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
- position_encoding: Some(match negotiated_encoding(config.caps()) {
- PositionEncoding::Utf8 => PositionEncodingKind::UTF8,
+ position_encoding: match negotiated_encoding(config.caps()) {
+ PositionEncoding::Utf8 => Some(PositionEncodingKind::UTF8),
PositionEncoding::Wide(wide) => match wide {
- WideEncoding::Utf16 => PositionEncodingKind::UTF16,
- WideEncoding::Utf32 => PositionEncodingKind::UTF32,
+ WideEncoding::Utf16 => Some(PositionEncodingKind::UTF16),
+ WideEncoding::Utf32 => Some(PositionEncodingKind::UTF32),
+ _ => None,
},
- }),
+ },
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
index cf51cf15a..c7b84c41b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -3,8 +3,9 @@
use std::mem;
use cfg::{CfgAtom, CfgExpr};
-use ide::{Cancellable, FileId, RunnableKind, TestId};
+use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId};
use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
+use rustc_hash::FxHashSet;
use vfs::AbsPathBuf;
use crate::global_state::GlobalStateSnapshot;
@@ -20,7 +21,9 @@ pub(crate) struct CargoTargetSpec {
pub(crate) package: String,
pub(crate) target: String,
pub(crate) target_kind: TargetKind,
+ pub(crate) crate_id: CrateId,
pub(crate) required_features: Vec<String>,
+ pub(crate) features: FxHashSet<String>,
}
impl CargoTargetSpec {
@@ -73,12 +76,13 @@ impl CargoTargetSpec {
}
}
- let target_required_features = if let Some(mut spec) = spec {
+ let (allowed_features, target_required_features) = if let Some(mut spec) = spec {
+ let allowed_features = mem::take(&mut spec.features);
let required_features = mem::take(&mut spec.required_features);
spec.push_to(&mut args, kind);
- required_features
+ (allowed_features, required_features)
} else {
- Vec::new()
+ (Default::default(), Default::default())
};
let cargo_config = snap.config.cargo();
@@ -97,7 +101,9 @@ impl CargoTargetSpec {
required_features(cfg, &mut feats);
}
- feats.extend(features.iter().cloned());
+ feats.extend(
+ features.iter().filter(|&feat| allowed_features.contains(feat)).cloned(),
+ );
feats.extend(target_required_features);
feats.dedup();
@@ -136,6 +142,8 @@ impl CargoTargetSpec {
target: target_data.name.clone(),
target_kind: target_data.kind,
required_features: target_data.required_features.clone(),
+ features: package_data.features.keys().cloned().collect(),
+ crate_id,
};
Ok(Some(res))
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
index d5d877680..e35201921 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
@@ -50,21 +50,24 @@ fn report_metric(metric: &str, value: u64, unit: &str) {
}
fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
- let mut mem = host.per_query_memory_usage();
+ let mem = host.per_query_memory_usage();
let before = profile::memory_usage();
drop(vfs);
let vfs = before.allocated - profile::memory_usage().allocated;
- mem.push(("VFS".into(), vfs));
let before = profile::memory_usage();
drop(host);
- mem.push(("Unaccounted".into(), before.allocated - profile::memory_usage().allocated));
+ let unaccounted = before.allocated - profile::memory_usage().allocated;
+ let remaining = profile::memory_usage().allocated;
- mem.push(("Remaining".into(), profile::memory_usage().allocated));
-
- for (name, bytes) in mem {
+ for (name, bytes, entries) in mem {
// NOTE: Not a debug print, so avoid going through the `eprintln` defined above.
- eprintln!("{bytes:>8} {name}");
+ eprintln!("{bytes:>8} {entries:>6} {name}");
}
+ eprintln!("{vfs:>8} VFS");
+
+ eprintln!("{unaccounted:>8} Unaccounted");
+
+ eprintln!("{remaining:>8} Remaining");
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 6ce1de5d3..4cb917ce2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -8,18 +8,20 @@ use std::{
use hir::{
db::{DefDatabase, ExpandDatabase, HirDatabase},
- AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
+ Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ModuleDef, Name,
};
use hir_def::{
body::{BodySourceMap, SyntheticSyntax},
- expr::{ExprId, PatId},
- FunctionId,
+ hir::{ExprId, PatId},
};
-use hir_ty::{Interner, TyExt, TypeFlags};
-use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
-use ide_db::base_db::{
- salsa::{self, debug::DebugQueryTable, ParallelDatabase},
- SourceDatabase, SourceDatabaseExt,
+use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
+use ide::{LineCol, RootDatabase};
+use ide_db::{
+ base_db::{
+ salsa::{self, debug::DebugQueryTable, ParallelDatabase},
+ SourceDatabase, SourceDatabaseExt,
+ },
+ LineIndexDatabase,
};
use itertools::Itertools;
use oorandom::Rand32;
@@ -27,7 +29,6 @@ use profile::{Bytes, StopWatch};
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
-use stdx::format_to;
use syntax::{AstNode, SyntaxNode};
use vfs::{AbsPathBuf, Vfs, VfsPath};
@@ -120,37 +121,80 @@ impl flags::AnalysisStats {
eprint!(" crates: {num_crates}");
let mut num_decls = 0;
- let mut funcs = Vec::new();
+ let mut bodies = Vec::new();
+ let mut adts = Vec::new();
+ let mut consts = Vec::new();
while let Some(module) = visit_queue.pop() {
if visited_modules.insert(module) {
visit_queue.extend(module.children(db));
for decl in module.declarations(db) {
num_decls += 1;
- if let ModuleDef::Function(f) = decl {
- funcs.push(f);
- }
+ match decl {
+ ModuleDef::Function(f) => bodies.push(DefWithBody::from(f)),
+ ModuleDef::Adt(a) => {
+ if let Adt::Enum(e) = a {
+ for v in e.variants(db) {
+ bodies.push(DefWithBody::from(v));
+ }
+ }
+ adts.push(a)
+ }
+ ModuleDef::Const(c) => {
+ bodies.push(DefWithBody::from(c));
+ consts.push(c)
+ }
+ ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
+ _ => (),
+ };
}
for impl_def in module.impl_defs(db) {
for item in impl_def.items(db) {
num_decls += 1;
- if let AssocItem::Function(f) = item {
- funcs.push(f);
+ match item {
+ AssocItem::Function(f) => bodies.push(DefWithBody::from(f)),
+ AssocItem::Const(c) => {
+ bodies.push(DefWithBody::from(c));
+ consts.push(c);
+ }
+ _ => (),
}
}
}
}
}
- eprintln!(", mods: {}, decls: {num_decls}, fns: {}", visited_modules.len(), funcs.len());
+ eprintln!(
+ ", mods: {}, decls: {num_decls}, bodies: {}, adts: {}, consts: {}",
+ visited_modules.len(),
+ bodies.len(),
+ adts.len(),
+ consts.len(),
+ );
eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed());
if self.randomize {
- shuffle(&mut rng, &mut funcs);
+ shuffle(&mut rng, &mut bodies);
+ }
+
+ if !self.skip_lowering {
+ self.run_body_lowering(db, &vfs, &bodies, verbosity);
}
if !self.skip_inference {
- self.run_inference(&host, db, &vfs, &funcs, verbosity);
+ self.run_inference(db, &vfs, &bodies, verbosity);
+ }
+
+ if !self.skip_mir_stats {
+ self.run_mir_lowering(db, &bodies, verbosity);
+ }
+
+ if !self.skip_data_layout {
+ self.run_data_layout(db, &adts, verbosity);
+ }
+
+ if !self.skip_const_eval {
+ self.run_const_eval(db, &consts, verbosity);
}
let total_span = analysis_sw.elapsed();
@@ -175,9 +219,8 @@ impl flags::AnalysisStats {
let mut total_macro_file_size = Bytes::default();
for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::<Vec<_>>() {
- if let Some((val, _)) = db.parse_macro_expansion(e.key).value {
- total_macro_file_size += syntax_len(val.syntax_node())
- }
+ let val = db.parse_macro_expansion(e.key).value.0;
+ total_macro_file_size += syntax_len(val.syntax_node())
}
eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
}
@@ -189,29 +232,122 @@ impl flags::AnalysisStats {
Ok(())
}
+ fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let mut all = 0;
+ let mut fail = 0;
+ for &a in adts {
+ if db.generic_params(a.into()).iter().next().is_some() {
+ // Data types with generics don't have layout.
+ continue;
+ }
+ all += 1;
+ let Err(e)
+ = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into())
+ else {
+ continue
+ };
+ if verbosity.is_spammy() {
+ let full_name = a
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(Some(a.name(db)))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Data layout for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let data_layout_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Data layouts:", data_layout_time);
+ eprintln!("Failed data layouts: {fail} ({}%)", percentage(fail, all));
+ report_metric("failed data layouts", fail, "#");
+ report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms");
+ }
+
+ fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let mut all = 0;
+ let mut fail = 0;
+ for &c in consts {
+ all += 1;
+ let Err(e) = c.render_eval(db) else {
+ continue;
+ };
+ if verbosity.is_spammy() {
+ let full_name = c
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(c.name(db))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Const eval for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let const_eval_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Const evaluation:", const_eval_time);
+ eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all));
+ report_metric("failed const evals", fail, "#");
+ report_metric("const eval time", const_eval_time.time.as_millis() as u64, "ms");
+ }
+
+ fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let all = bodies.len() as u64;
+ let mut fail = 0;
+ for &body in bodies {
+ let Err(e) = db.mir_body(body.into()) else {
+ continue;
+ };
+ if verbosity.is_spammy() {
+ let full_name = body
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(Some(body.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Mir body for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let mir_lowering_time = sw.elapsed();
+ eprintln!("{:<20} {}", "MIR lowering:", mir_lowering_time);
+ eprintln!("Mir failed bodies: {fail} ({}%)", percentage(fail, all));
+ report_metric("mir failed bodies", fail, "#");
+ report_metric("mir lowering time", mir_lowering_time.time.as_millis() as u64, "ms");
+ }
+
fn run_inference(
&self,
- host: &AnalysisHost,
db: &RootDatabase,
vfs: &Vfs,
- funcs: &[Function],
+ bodies: &[DefWithBody],
verbosity: Verbosity,
) {
let mut bar = match verbosity {
Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
_ if self.parallel || self.output.is_some() => ProgressReport::hidden(),
- _ => ProgressReport::new(funcs.len() as u64),
+ _ => ProgressReport::new(bodies.len() as u64),
};
if self.parallel {
let mut inference_sw = self.stop_watch();
let snap = Snap(db.snapshot());
- funcs
+ bodies
.par_iter()
- .map_with(snap, |snap, &f| {
- let f_id = FunctionId::from(f);
- snap.0.body(f_id.into());
- snap.0.infer(f_id.into());
+ .map_with(snap, |snap, &body| {
+ snap.0.body(body.into());
+ snap.0.infer(body.into());
})
.count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@@ -227,38 +363,58 @@ impl flags::AnalysisStats {
let mut num_pats_unknown = 0;
let mut num_pats_partially_unknown = 0;
let mut num_pat_type_mismatches = 0;
- let analysis = host.analysis();
- for f in funcs.iter().copied() {
- let name = f.name(db);
- let full_name = f
- .module(db)
- .path_to_root(db)
- .into_iter()
- .rev()
- .filter_map(|it| it.name(db))
- .chain(Some(f.name(db)))
- .join("::");
+ for &body_id in bodies {
+ let name = body_id.name(db).unwrap_or_else(Name::missing);
+ let module = body_id.module(db);
+ let full_name = move || {
+ module
+ .krate()
+ .display_name(db)
+ .map(|it| it.canonical_name().to_string())
+ .into_iter()
+ .chain(
+ module
+ .path_to_root(db)
+ .into_iter()
+ .filter_map(|it| it.name(db))
+ .rev()
+ .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string()),
+ )
+ .join("::")
+ };
if let Some(only_name) = self.only.as_deref() {
- if name.to_string() != only_name && full_name != only_name {
+ if name.display(db).to_string() != only_name && full_name() != only_name {
continue;
}
}
- let mut msg = format!("processing: {full_name}");
- if verbosity.is_verbose() {
- if let Some(src) = f.source(db) {
- let original_file = src.file_id.original_file(db);
- let path = vfs.file_path(original_file);
- let syntax_range = src.value.syntax().text_range();
- format_to!(msg, " ({} {:?})", path, syntax_range);
+ let msg = move || {
+ if verbosity.is_verbose() {
+ let source = match body_id {
+ DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::InTypeConst(_) => unimplemented!(),
+ };
+ if let Some(src) = source {
+ let original_file = src.file_id.original_file(db);
+ let path = vfs.file_path(original_file);
+ let syntax_range = src.value.text_range();
+ format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+ } else {
+ format!("processing: {}", full_name())
+ }
+ } else {
+ format!("processing: {}", full_name())
}
- }
+ };
if verbosity.is_spammy() {
- bar.println(msg.to_string());
+ bar.println(msg());
}
- bar.set_message(&msg);
- let f_id = FunctionId::from(f);
- let (body, sm) = db.body_with_source_map(f_id.into());
- let inference_result = db.infer(f_id.into());
+ bar.set_message(msg);
+ let (body, sm) = db.body_with_source_map(body_id.into());
+ let inference_result = db.infer(body_id.into());
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
@@ -269,9 +425,7 @@ impl flags::AnalysisStats {
let unknown_or_partial = if ty.is_unknown() {
num_exprs_unknown += 1;
if verbosity.is_spammy() {
- if let Some((path, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Unknown type",
path,
@@ -281,7 +435,7 @@ impl flags::AnalysisStats {
end.col,
));
} else {
- bar.println(format!("{name}: Unknown type",));
+ bar.println(format!("{}: Unknown type", name.display(db)));
}
}
true
@@ -295,9 +449,7 @@ impl flags::AnalysisStats {
};
if self.only.is_some() && verbosity.is_spammy() {
// in super-verbose mode for just one function, we print every single expression
- if let Some((_, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((_, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{}:{}-{}:{}: {}",
start.line + 1,
@@ -313,16 +465,14 @@ impl flags::AnalysisStats {
if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
println!(
r#"{},type,"{}""#,
- location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+ location_csv_expr(db, vfs, &sm, expr_id),
ty.display(db)
);
}
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
num_expr_type_mismatches += 1;
if verbosity.is_verbose() {
- if let Some((path, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
path,
@@ -336,7 +486,7 @@ impl flags::AnalysisStats {
} else {
bar.println(format!(
"{}: Expected {}, got {}",
- name,
+ name.display(db),
mismatch.expected.display(db),
mismatch.actual.display(db)
));
@@ -345,7 +495,7 @@ impl flags::AnalysisStats {
if self.output == Some(OutputFormat::Csv) {
println!(
r#"{},mismatch,"{}","{}""#,
- location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+ location_csv_expr(db, vfs, &sm, expr_id),
mismatch.expected.display(db),
mismatch.actual.display(db)
);
@@ -355,7 +505,7 @@ impl flags::AnalysisStats {
if verbosity.is_spammy() {
bar.println(format!(
"In {}: {} exprs, {} unknown, {} partial",
- full_name,
+ full_name(),
num_exprs - previous_exprs,
num_exprs_unknown - previous_unknown,
num_exprs_partially_unknown - previous_partially_unknown
@@ -372,9 +522,7 @@ impl flags::AnalysisStats {
let unknown_or_partial = if ty.is_unknown() {
num_pats_unknown += 1;
if verbosity.is_spammy() {
- if let Some((path, start, end)) =
- pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Unknown type",
path,
@@ -384,7 +532,7 @@ impl flags::AnalysisStats {
end.col,
));
} else {
- bar.println(format!("{name}: Unknown type",));
+ bar.println(format!("{}: Unknown type", name.display(db)));
}
}
true
@@ -398,8 +546,7 @@ impl flags::AnalysisStats {
};
if self.only.is_some() && verbosity.is_spammy() {
// in super-verbose mode for just one function, we print every single pattern
- if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((_, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{}:{}-{}:{}: {}",
start.line + 1,
@@ -415,16 +562,14 @@ impl flags::AnalysisStats {
if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
println!(
r#"{},type,"{}""#,
- location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ location_csv_pat(db, vfs, &sm, pat_id),
ty.display(db)
);
}
if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
num_pat_type_mismatches += 1;
if verbosity.is_verbose() {
- if let Some((path, start, end)) =
- pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
path,
@@ -438,7 +583,7 @@ impl flags::AnalysisStats {
} else {
bar.println(format!(
"{}: Expected {}, got {}",
- name,
+ name.display(db),
mismatch.expected.display(db),
mismatch.actual.display(db)
));
@@ -447,7 +592,7 @@ impl flags::AnalysisStats {
if self.output == Some(OutputFormat::Csv) {
println!(
r#"{},mismatch,"{}","{}""#,
- location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ location_csv_pat(db, vfs, &sm, pat_id),
mismatch.expected.display(db),
mismatch.actual.display(db)
);
@@ -457,7 +602,7 @@ impl flags::AnalysisStats {
if verbosity.is_spammy() {
bar.println(format!(
"In {}: {} pats, {} unknown, {} partial",
- full_name,
+ full_name(),
num_pats - previous_pats,
num_pats_unknown - previous_unknown,
num_pats_partially_unknown - previous_partially_unknown
@@ -468,6 +613,7 @@ impl flags::AnalysisStats {
}
bar.finish_and_clear();
+ let inference_time = inference_sw.elapsed();
eprintln!(
" exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
num_exprs,
@@ -486,12 +632,89 @@ impl flags::AnalysisStats {
percentage(num_pats_partially_unknown, num_pats),
num_pat_type_mismatches
);
+ eprintln!("{:<20} {}", "Inference:", inference_time);
report_metric("unknown type", num_exprs_unknown, "#");
report_metric("type mismatches", num_expr_type_mismatches, "#");
report_metric("pattern unknown type", num_pats_unknown, "#");
report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
+ report_metric("inference time", inference_time.time.as_millis() as u64, "ms");
+ }
- eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
+ fn run_body_lowering(
+ &self,
+ db: &RootDatabase,
+ vfs: &Vfs,
+ bodies: &[DefWithBody],
+ verbosity: Verbosity,
+ ) {
+ let mut bar = match verbosity {
+ Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
+ _ if self.output.is_some() => ProgressReport::hidden(),
+ _ => ProgressReport::new(bodies.len() as u64),
+ };
+
+ let mut sw = self.stop_watch();
+ bar.tick();
+ for &body_id in bodies {
+ let module = body_id.module(db);
+ let full_name = move || {
+ module
+ .krate()
+ .display_name(db)
+ .map(|it| it.canonical_name().to_string())
+ .into_iter()
+ .chain(
+ module
+ .path_to_root(db)
+ .into_iter()
+ .filter_map(|it| it.name(db))
+ .rev()
+ .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string()),
+ )
+ .join("::")
+ };
+ if let Some(only_name) = self.only.as_deref() {
+ if body_id.name(db).unwrap_or_else(Name::missing).display(db).to_string()
+ != only_name
+ && full_name() != only_name
+ {
+ continue;
+ }
+ }
+ let msg = move || {
+ if verbosity.is_verbose() {
+ let source = match body_id {
+ DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::InTypeConst(_) => unimplemented!(),
+ };
+ if let Some(src) = source {
+ let original_file = src.file_id.original_file(db);
+ let path = vfs.file_path(original_file);
+ let syntax_range = src.value.text_range();
+ format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+ } else {
+ format!("processing: {}", full_name())
+ }
+ } else {
+ format!("processing: {}", full_name())
+ }
+ };
+ if verbosity.is_spammy() {
+ bar.println(msg());
+ }
+ bar.set_message(msg);
+ db.body_with_source_map(body_id.into());
+ bar.inc(1);
+ }
+
+ bar.finish_and_clear();
+ let body_lowering_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Body lowering:", body_lowering_time);
+ report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms");
}
fn stop_watch(&self) -> StopWatch {
@@ -499,46 +722,34 @@ impl flags::AnalysisStats {
}
}
-fn location_csv_expr(
- db: &RootDatabase,
- analysis: &Analysis,
- vfs: &Vfs,
- sm: &BodySourceMap,
- expr_id: ExprId,
-) -> String {
+fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: ExprId) -> String {
let src = match sm.expr_syntax(expr_id) {
Ok(s) => s,
Err(SyntheticSyntax) => return "synthetic,,".to_string(),
};
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
}
-fn location_csv_pat(
- db: &RootDatabase,
- analysis: &Analysis,
- vfs: &Vfs,
- sm: &BodySourceMap,
- pat_id: PatId,
-) -> String {
+fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: PatId) -> String {
let src = match sm.pat_syntax(pat_id) {
Ok(s) => s,
Err(SyntheticSyntax) => return "synthetic,,".to_string(),
};
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| {
e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone())
});
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -547,18 +758,17 @@ fn location_csv_pat(
fn expr_syntax_range(
db: &RootDatabase,
- analysis: &Analysis,
vfs: &Vfs,
sm: &BodySourceMap,
expr_id: ExprId,
) -> Option<(VfsPath, LineCol, LineCol)> {
let src = sm.expr_syntax(expr_id);
if let Ok(src) = src {
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -569,14 +779,13 @@ fn expr_syntax_range(
}
fn pat_syntax_range(
db: &RootDatabase,
- analysis: &Analysis,
vfs: &Vfs,
sm: &BodySourceMap,
pat_id: PatId,
) -> Option<(VfsPath, LineCol, LineCol)> {
let src = sm.pat_syntax(pat_id);
if let Ok(src) = src {
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| {
e.either(
|it| it.to_node(&root).syntax().clone(),
@@ -585,7 +794,7 @@ fn pat_syntax_range(
});
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 4006d023d..4306d7212 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -17,9 +17,15 @@ impl flags::Diagnostics {
pub fn run(self) -> anyhow::Result<()> {
let mut cargo_config = CargoConfig::default();
cargo_config.sysroot = Some(RustLibSource::Discover);
+ let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
+ let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&p));
+ ProcMacroServerChoice::Explicit(path)
+ } else {
+ ProcMacroServerChoice::Sysroot
+ };
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: !self.disable_build_scripts,
- with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+ with_proc_macro_server,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 770612cc9..208a4e6ec 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -78,8 +78,16 @@ xflags::xflags! {
optional --disable-build-scripts
/// Don't use expand proc macros.
optional --disable-proc-macros
- /// Only resolve names, don't run type inference.
+ /// Skip body lowering.
+ optional --skip-lowering
+ /// Skip type inference.
optional --skip-inference
+ /// Skip lowering to mir
+ optional --skip-mir-stats
+ /// Skip data layout calculation
+ optional --skip-data-layout
+ /// Skip const evaluation
+ optional --skip-const-eval
}
cmd diagnostics {
@@ -90,6 +98,8 @@ xflags::xflags! {
optional --disable-build-scripts
/// Don't use expand proc macros.
optional --disable-proc-macros
+ /// Run a custom proc-macro-srv binary.
+ optional --proc-macro-srv path: PathBuf
}
cmd ssr {
@@ -104,14 +114,15 @@ xflags::xflags! {
optional --debug snippet: String
}
- cmd proc-macro {}
-
cmd lsif {
required path: PathBuf
}
cmd scip {
required path: PathBuf
+
+ /// The output path where the SCIP file will be written to. Defaults to `index.scip`.
+ optional --output path: PathBuf
}
}
}
@@ -139,7 +150,6 @@ pub enum RustAnalyzerCmd {
Diagnostics(Diagnostics),
Ssr(Ssr),
Search(Search),
- ProcMacro(ProcMacro),
Lsif(Lsif),
Scip(Scip),
}
@@ -172,12 +182,16 @@ pub struct AnalysisStats {
pub parallel: bool,
pub memory_usage: bool,
pub source_stats: bool,
+ pub skip_lowering: bool,
+ pub skip_inference: bool,
+ pub skip_mir_stats: bool,
+ pub skip_data_layout: bool,
+ pub skip_const_eval: bool,
pub only: Option<String>,
pub with_deps: bool,
pub no_sysroot: bool,
pub disable_build_scripts: bool,
pub disable_proc_macros: bool,
- pub skip_inference: bool,
}
#[derive(Debug)]
@@ -186,6 +200,7 @@ pub struct Diagnostics {
pub disable_build_scripts: bool,
pub disable_proc_macros: bool,
+ pub proc_macro_srv: Option<PathBuf>,
}
#[derive(Debug)]
@@ -201,9 +216,6 @@ pub struct Search {
}
#[derive(Debug)]
-pub struct ProcMacro;
-
-#[derive(Debug)]
pub struct Lsif {
pub path: PathBuf,
}
@@ -211,6 +223,7 @@ pub struct Lsif {
#[derive(Debug)]
pub struct Scip {
pub path: PathBuf,
+ pub output: Option<PathBuf>,
}
impl RustAnalyzer {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
index 5a958d963..4e8f99971 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -1,14 +1,17 @@
//! Loads a Cargo project into a static instance of analysis, without support
//! for incorporating changes.
-use std::{convert::identity, path::Path, sync::Arc};
+use std::path::Path;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use crossbeam_channel::{unbounded, Receiver};
-use hir::db::DefDatabase;
use ide::{AnalysisHost, Change};
-use ide_db::{base_db::CrateGraph, FxHashMap};
+use ide_db::{
+ base_db::{CrateGraph, ProcMacros},
+ FxHashMap,
+};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use triomphe::Arc;
use vfs::{loader::Handle, AbsPath, AbsPathBuf};
use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig};
@@ -24,7 +27,7 @@ pub struct LoadCargoConfig {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProcMacroServerChoice {
Sysroot,
- Explicit(AbsPathBuf, Vec<String>),
+ Explicit(AbsPathBuf),
None,
}
@@ -66,23 +69,17 @@ pub fn load_workspace(
Box::new(loader)
};
- let proc_macro_client = match &load_config.with_proc_macro_server {
+ let proc_macro_server = match &load_config.with_proc_macro_server {
ProcMacroServerChoice::Sysroot => ws
.find_sysroot_proc_macro_srv()
- .ok_or_else(|| "failed to find sysroot proc-macro server".to_owned())
- .and_then(|it| {
- ProcMacroServer::spawn(it, identity::<&[&str]>(&[])).map_err(|e| e.to_string())
- }),
- ProcMacroServerChoice::Explicit(path, args) => {
- ProcMacroServer::spawn(path.clone(), args).map_err(|e| e.to_string())
+ .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)),
+ ProcMacroServerChoice::Explicit(path) => {
+ ProcMacroServer::spawn(path.clone()).map_err(Into::into)
}
- ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()),
+ ProcMacroServerChoice::None => Err(anyhow!("proc macro server disabled")),
};
- let crate_graph = ws.to_crate_graph(
- &mut |_, path: &AbsPath| {
- load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[])
- },
+ let (crate_graph, proc_macros) = ws.to_crate_graph(
&mut |path: &AbsPath| {
let contents = loader.load_sync(path);
let path = vfs::VfsPath::from(path.to_path_buf());
@@ -91,6 +88,28 @@ pub fn load_workspace(
},
extra_env,
);
+ let proc_macros = {
+ let proc_macro_server = match &proc_macro_server {
+ Ok(it) => Ok(it),
+ Err(e) => Err(e.to_string()),
+ };
+ proc_macros
+ .into_iter()
+ .map(|(crate_id, path)| {
+ (
+ crate_id,
+ path.map_or_else(
+ |_| Err("proc macro crate is missing dylib".to_owned()),
+ |(_, path)| {
+ proc_macro_server.as_ref().map_err(Clone::clone).and_then(
+ |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
+ )
+ },
+ ),
+ )
+ })
+ .collect()
+ };
let project_folders = ProjectFolders::new(&[ws], &[]);
loader.set_config(vfs::loader::Config {
@@ -100,17 +119,23 @@ pub fn load_workspace(
});
tracing::debug!("crate graph: {:?}", crate_graph);
- let host =
- load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
+ let host = load_crate_graph(
+ crate_graph,
+ proc_macros,
+ project_folders.source_root_config,
+ &mut vfs,
+ &receiver,
+ );
if load_config.prefill_caches {
host.analysis().parallel_prime_caches(1, |_| {})?;
}
- Ok((host, vfs, proc_macro_client.ok()))
+ Ok((host, vfs, proc_macro_server.ok()))
}
fn load_crate_graph(
crate_graph: CrateGraph,
+ proc_macros: ProcMacros,
source_root_config: SourceRootConfig,
vfs: &mut vfs::Vfs,
receiver: &Receiver<vfs::loader::Message>,
@@ -119,7 +144,7 @@ fn load_crate_graph(
let mut host = AnalysisHost::new(lru_cap);
let mut analysis_change = Change::new();
- host.raw_database_mut().set_enable_proc_attr_macros(true);
+ host.raw_database_mut().enable_proc_attr_macros();
// wait until Vfs has loaded all roots
for task in receiver {
@@ -139,9 +164,9 @@ fn load_crate_graph(
let changes = vfs.take_changes();
for file in changes {
if file.exists() {
- let contents = vfs.file_contents(file.file_id).to_vec();
- if let Ok(text) = String::from_utf8(contents) {
- analysis_change.change_file(file.file_id, Some(Arc::new(text)))
+ let contents = vfs.file_contents(file.file_id);
+ if let Ok(text) = std::str::from_utf8(contents) {
+ analysis_change.change_file(file.file_id, Some(Arc::from(text)))
}
}
}
@@ -149,6 +174,7 @@ fn load_crate_graph(
analysis_change.set_roots(source_roots);
analysis_change.set_crate_graph(crate_graph);
+ analysis_change.set_proc_macros(proc_macros);
host.apply_change(analysis_change);
host
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
index d459dd115..c236f9c7f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
@@ -4,41 +4,29 @@
use std::io::{self, Write};
/// A Simple ASCII Progress Bar
-pub(crate) struct ProgressReport {
+pub(crate) struct ProgressReport<'a> {
curr: f32,
text: String,
hidden: bool,
len: u64,
pos: u64,
- msg: String,
+ msg: Option<Box<dyn Fn() -> String + 'a>>,
}
-impl ProgressReport {
- pub(crate) fn new(len: u64) -> ProgressReport {
- ProgressReport {
- curr: 0.0,
- text: String::new(),
- hidden: false,
- len,
- pos: 0,
- msg: String::new(),
- }
+impl<'a> ProgressReport<'a> {
+ pub(crate) fn new(len: u64) -> ProgressReport<'a> {
+ ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None }
}
- pub(crate) fn hidden() -> ProgressReport {
- ProgressReport {
- curr: 0.0,
- text: String::new(),
- hidden: true,
- len: 0,
- pos: 0,
- msg: String::new(),
- }
+ pub(crate) fn hidden() -> ProgressReport<'a> {
+ ProgressReport { curr: 0.0, text: String::new(), hidden: true, len: 0, pos: 0, msg: None }
}
- pub(crate) fn set_message(&mut self, msg: &str) {
- self.msg = msg.to_string();
+ pub(crate) fn set_message(&mut self, msg: impl Fn() -> String + 'a) {
+ if !self.hidden {
+ self.msg = Some(Box::new(msg));
+ }
self.tick();
}
@@ -67,7 +55,12 @@ impl ProgressReport {
return;
}
let percent = (self.curr * 100.0) as u32;
- let text = format!("{}/{} {percent:3>}% {}", self.pos, self.len, self.msg);
+ let text = format!(
+ "{}/{} {percent:3>}% {}",
+ self.pos,
+ self.len,
+ self.msg.as_ref().map_or_else(|| String::new(), |it| it())
+ );
self.update_text(&text);
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 3e5e40750..b0b724bdf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -2,6 +2,7 @@
use std::{
collections::{HashMap, HashSet},
+ path::PathBuf,
time::Instant,
};
@@ -9,7 +10,6 @@ use crate::{
cli::load_cargo::ProcMacroServerChoice,
line_index::{LineEndings, LineIndex, PositionEncoding},
};
-use hir::Name;
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
TokenStaticData,
@@ -66,7 +66,6 @@ impl flags::Scip {
.as_os_str()
.to_str()
.ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
- .to_string()
),
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
special_fields: Default::default(),
@@ -167,7 +166,8 @@ impl flags::Scip {
special_fields: Default::default(),
};
- scip::write_message_to_file("index.scip", index)
+ let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip"));
+ scip::write_message_to_file(out_path, index)
.map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
eprintln!("Generating SCIP finished {:?}", now.elapsed());
@@ -210,13 +210,12 @@ fn new_descriptor_str(
}
}
-fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
- let mut name = name.to_string();
- if name.contains("'") {
- name = format!("`{name}`");
+fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
+ if name.contains('\'') {
+ new_descriptor_str(&format!("`{name}`"), suffix)
+ } else {
+ new_descriptor_str(&name, suffix)
}
-
- new_descriptor_str(name.as_str(), suffix)
}
/// Loosely based on `def_to_moniker`
@@ -236,7 +235,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
.iter()
.map(|desc| {
new_descriptor(
- desc.name.clone(),
+ &desc.name,
match desc.desc {
MonikerDescriptorKind::Namespace => Namespace,
MonikerDescriptorKind::Type => Type,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index c35cce103..6355c620f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -7,13 +7,14 @@
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.
-use std::{fmt, iter, path::PathBuf};
+use std::{fmt, iter, ops::Not, path::PathBuf};
+use cfg::{CfgAtom, CfgDiff};
use flycheck::FlycheckConfig;
use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
- JoinLinesConfig, Snippet, SnippetScope,
+ JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -23,11 +24,10 @@ use itertools::Itertools;
use lsp_types::{ClientCapabilities, MarkupKind};
use project_model::{
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
- UnsetTestCrates,
};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{de::DeserializeOwned, Deserialize};
-use vfs::AbsPathBuf;
+use vfs::{AbsPath, AbsPathBuf};
use crate::{
caps::completion_item_edit_resolve,
@@ -101,6 +101,8 @@ config_data! {
/// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = "true",
+ /// List of cfg options to enable with the given values.
+ cargo_cfgs: FxHashMap<String, String> = "{}",
/// Extra arguments that are passed to every cargo invocation.
cargo_extraArgs: Vec<String> = "[]",
/// Extra environment variables that will be set when running cargo, rustc
@@ -128,7 +130,7 @@ config_data! {
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
// than `checkOnSave_target`
cargo_target: Option<String> = "null",
- /// Unsets `#[cfg(test)]` for the specified crates.
+ /// Unsets the implicit `#[cfg(test)]` for the specified crates.
cargo_unsetTest: Vec<String> = "[\"core\"]",
/// Run the check command for diagnostics on save.
@@ -281,6 +283,8 @@ config_data! {
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = "true",
+ /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
+ highlightRelated_closureCaptures_enable: bool = "true",
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
highlightRelated_exitPoints_enable: bool = "true",
/// Enables highlighting of related references while the cursor is on any identifier.
@@ -311,8 +315,18 @@ config_data! {
/// Whether to show keyword hover popups. Only applies when
/// `#rust-analyzer.hover.documentation.enable#` is set.
hover_documentation_keywords_enable: bool = "true",
- /// Use markdown syntax for links in hover.
+ /// Use markdown syntax for links on hover.
hover_links_enable: bool = "true",
+ /// How to render the align information in a memory layout hover.
+ hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
+ /// Whether to show memory layout data on hover.
+ hover_memoryLayout_enable: bool = "true",
+ /// How to render the niche information in a memory layout hover.
+ hover_memoryLayout_niches: Option<bool> = "false",
+ /// How to render the offset information in a memory layout hover.
+ hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
+ /// How to render the size information in a memory layout hover.
+ hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
/// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
imports_granularity_enforce: bool = "false",
@@ -336,8 +350,12 @@ config_data! {
/// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
/// to always show them).
inlayHints_closingBraceHints_minLines: usize = "25",
+ /// Whether to show inlay hints for closure captures.
+ inlayHints_closureCaptureHints_enable: bool = "false",
/// Whether to show inlay type hints for return types of closures.
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"",
+ /// Closure notation in type and chaining inlay hints.
+ inlayHints_closureStyle: ClosureStyle = "\"impl_fn\"",
/// Whether to show enum variant discriminant hints.
inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"",
/// Whether to show inlay hints for type adjustments.
@@ -418,6 +436,8 @@ config_data! {
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
lru_capacity: Option<usize> = "null",
+ /// Sets the LRU capacity of the specified queries.
+ lru_query_capacities: FxHashMap<Box<str>, usize> = "{}",
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = "true",
@@ -433,8 +453,7 @@ config_data! {
///
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
- /// Internal config, path to proc-macro server executable (typically,
- /// this is rust-analyzer itself, but we override this in tests).
+ /// Internal config, path to proc-macro server executable.
procMacro_server: Option<PathBuf> = "null",
/// Exclude imports from find-all-references.
@@ -474,6 +493,8 @@ config_data! {
/// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
/// doc links.
semanticHighlighting_doc_comment_inject_enable: bool = "true",
+ /// Whether the server is allowed to emit non-standard tokens and modifiers.
+ semanticHighlighting_nonStandardTokens: bool = "true",
/// Use semantic tokens for operators.
///
/// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
@@ -484,7 +505,7 @@ config_data! {
/// When enabled, rust-analyzer will emit special token types for operator tokens instead
/// of the generic `operator` token type.
semanticHighlighting_operator_specialization_enable: bool = "false",
- /// Use semantic tokens for punctuations.
+ /// Use semantic tokens for punctuation.
///
/// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
/// they are tagged with modifiers or have a special role.
@@ -492,7 +513,7 @@ config_data! {
/// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
/// calls.
semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
- /// Use specialized semantic tokens for punctuations.
+ /// Use specialized semantic tokens for punctuation.
///
/// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
/// of the generic `punctuation` token type.
@@ -531,8 +552,9 @@ impl Default for ConfigData {
#[derive(Debug, Clone)]
pub struct Config {
- pub discovered_projects: Option<Vec<ProjectManifest>>,
- pub workspace_roots: Vec<AbsPathBuf>,
+ discovered_projects: Vec<ProjectManifest>,
+ /// The workspace roots as registered by the LSP client
+ workspace_roots: Vec<AbsPathBuf>,
caps: lsp_types::ClientCapabilities,
root_path: AbsPathBuf,
data: ConfigData,
@@ -570,6 +592,7 @@ pub struct LensConfig {
// runnables
pub run: bool,
pub debug: bool,
+ pub interpret: bool,
// implementations
pub implementations: bool,
@@ -707,11 +730,11 @@ pub struct ClientCommandsConfig {
}
#[derive(Debug)]
-pub struct ConfigUpdateError {
+pub struct ConfigError {
errors: Vec<(String, serde_json::Error)>,
}
-impl fmt::Display for ConfigUpdateError {
+impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let errors = self.errors.iter().format_with("\n", |(key, e), f| {
f(key)?;
@@ -720,8 +743,7 @@ impl fmt::Display for ConfigUpdateError {
});
write!(
f,
- "rust-analyzer found {} invalid config value{}:\n{}",
- self.errors.len(),
+ "invalid config value{}:\n{}",
if self.errors.len() == 1 { "" } else { "s" },
errors
)
@@ -738,7 +760,7 @@ impl Config {
caps,
data: ConfigData::default(),
detached_files: Vec::new(),
- discovered_projects: None,
+ discovered_projects: Vec::new(),
root_path,
snippets: Default::default(),
workspace_roots,
@@ -751,10 +773,20 @@ impl Config {
if discovered.is_empty() {
tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
}
- self.discovered_projects = Some(discovered);
+ self.discovered_projects = discovered;
}
- pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
+ pub fn remove_workspace(&mut self, path: &AbsPath) {
+ if let Some(position) = self.workspace_roots.iter().position(|it| it == path) {
+ self.workspace_roots.remove(position);
+ }
+ }
+
+ pub fn add_workspaces(&mut self, paths: impl Iterator<Item = AbsPathBuf>) {
+ self.workspace_roots.extend(paths);
+ }
+
+ pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError> {
tracing::info!("updating config from JSON: {:#}", json);
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
return Ok(());
@@ -801,7 +833,7 @@ impl Config {
if errors.is_empty() {
Ok(())
} else {
- Err(ConfigUpdateError { errors })
+ Err(ConfigError { errors })
}
}
@@ -856,25 +888,19 @@ impl Config {
pub fn linked_projects(&self) -> Vec<LinkedProject> {
match self.data.linkedProjects.as_slice() {
[] => {
- match self.discovered_projects.as_ref() {
- Some(discovered_projects) => {
- let exclude_dirs: Vec<_> = self
- .data
- .files_excludeDirs
- .iter()
- .map(|p| self.root_path.join(p))
- .collect();
- discovered_projects
- .iter()
- .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| {
+ let exclude_dirs: Vec<_> =
+ self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
+ self.discovered_projects
+ .iter()
+ .filter(
+ |(ProjectManifest::ProjectJson(path)
+ | ProjectManifest::CargoToml(path))| {
!exclude_dirs.iter().any(|p| path.starts_with(p))
- })
- .cloned()
- .map(LinkedProject::from)
- .collect()
- }
- None => Vec::new(),
- }
+ },
+ )
+ .cloned()
+ .map(LinkedProject::from)
+ .collect()
}
linked_projects => linked_projects
.iter()
@@ -1013,6 +1039,11 @@ impl Config {
.is_some()
}
+ pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool {
+ try_!(self.caps.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens?)
+ .unwrap_or(false)
+ }
+
pub fn position_encoding(&self) -> PositionEncoding {
negotiated_encoding(&self.caps)
}
@@ -1025,6 +1056,10 @@ impl Config {
self.experimental("codeActionGroup")
}
+ pub fn local_docs(&self) -> bool {
+ self.experimental("localDocs")
+ }
+
pub fn open_server_logs(&self) -> bool {
self.experimental("openServerLogs")
}
@@ -1085,27 +1120,27 @@ impl Config {
extra_env
}
- pub fn lru_capacity(&self) -> Option<usize> {
+ pub fn lru_parse_query_capacity(&self) -> Option<usize> {
self.data.lru_capacity
}
- pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> {
- if !self.data.procMacro_enable {
- return None;
- }
- Some(match &self.data.procMacro_server {
- Some(it) => (
- AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)),
- true,
- ),
- None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false),
- })
+ pub fn lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>> {
+ self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities)
+ }
+
+ pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
+ let path = self.data.procMacro_server.clone()?;
+ Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(&path)))
}
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
&self.data.procMacro_ignored
}
+ pub fn expand_proc_macros(&self) -> bool {
+ self.data.procMacro_enable
+ }
+
pub fn expand_proc_attr_macros(&self) -> bool {
self.data.procMacro_enable && self.data.procMacro_attributes_enable
}
@@ -1164,7 +1199,34 @@ impl Config {
sysroot,
sysroot_src,
rustc_source,
- unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
+ cfg_overrides: project_model::CfgOverrides {
+ global: CfgDiff::new(
+ self.data
+ .cargo_cfgs
+ .iter()
+ .map(|(key, val)| {
+ if val.is_empty() {
+ CfgAtom::Flag(key.into())
+ } else {
+ CfgAtom::KeyValue { key: key.into(), value: val.into() }
+ }
+ })
+ .collect(),
+ vec![],
+ )
+ .unwrap(),
+ selective: self
+ .data
+ .cargo_unsetTest
+ .iter()
+ .map(|it| {
+ (
+ it.clone(),
+ CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(),
+ )
+ })
+ .collect(),
+ },
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
InvocationStrategy::Once => project_model::InvocationStrategy::Once,
@@ -1291,6 +1353,13 @@ impl Config {
hide_closure_initialization_hints: self
.data
.inlayHints_typeHints_hideClosureInitialization,
+ closure_style: match self.data.inlayHints_closureStyle {
+ ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
+ ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
+ ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
+ ClosureStyle::Hide => hir::ClosureStyle::Hide,
+ },
+ closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable,
adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
@@ -1409,6 +1478,9 @@ impl Config {
LensConfig {
run: self.data.lens_enable && self.data.lens_run_enable,
debug: self.data.lens_enable && self.data.lens_debug_enable,
+ interpret: self.data.lens_enable
+ && self.data.lens_run_enable
+ && self.data.interpret_tests,
implementations: self.data.lens_enable && self.data.lens_implementations_enable,
method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
@@ -1430,6 +1502,10 @@ impl Config {
}
}
+ pub fn highlighting_non_standard_tokens(&self) -> bool {
+ self.data.semanticHighlighting_nonStandardTokens
+ }
+
pub fn highlighting_config(&self) -> HighlightConfig {
HighlightConfig {
strings: self.data.semanticHighlighting_strings_enable,
@@ -1446,8 +1522,19 @@ impl Config {
}
pub fn hover(&self) -> HoverConfig {
+ let mem_kind = |kind| match kind {
+ MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
+ MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
+ MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
+ };
HoverConfig {
links_in_hover: self.data.hover_links_enable,
+ memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig {
+ size: self.data.hover_memoryLayout_size.map(mem_kind),
+ offset: self.data.hover_memoryLayout_offset.map(mem_kind),
+ alignment: self.data.hover_memoryLayout_alignment.map(mem_kind),
+ niches: self.data.hover_memoryLayout_niches.unwrap_or_default(),
+ }),
documentation: self.data.hover_documentation_enable,
format: {
let is_markdown = try_or_def!(self
@@ -1467,7 +1554,6 @@ impl Config {
}
},
keywords: self.data.hover_documentation_keywords_enable,
- interpret_tests: self.data.interpret_tests,
}
}
@@ -1537,6 +1623,7 @@ impl Config {
break_points: self.data.highlightRelated_breakPoints_enable,
exit_points: self.data.highlightRelated_exitPoints_enable,
yield_points: self.data.highlightRelated_yieldPoints_enable,
+ closure_captures: self.data.highlightRelated_closureCaptures_enable,
}
}
@@ -1657,6 +1744,9 @@ mod de_unit_v {
named_unit_variant!(reborrow);
named_unit_variant!(fieldless);
named_unit_variant!(with_block);
+ named_unit_variant!(decimal);
+ named_unit_variant!(hexadecimal);
+ named_unit_variant!(both);
}
#[derive(Deserialize, Debug, Clone, Copy)]
@@ -1798,6 +1888,15 @@ enum ClosureReturnTypeHintsDef {
}
#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum ClosureStyle {
+ ImplFn,
+ RustAnalyzer,
+ WithId,
+ Hide,
+}
+
+#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
enum ReborrowHintsDef {
#[serde(deserialize_with = "true_or_always")]
@@ -1878,6 +1977,18 @@ enum WorkspaceSymbolSearchKindDef {
AllSymbols,
}
+#[derive(Deserialize, Debug, Copy, Clone)]
+#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
+pub enum MemoryLayoutHoverRenderKindDef {
+ #[serde(deserialize_with = "de_unit_v::decimal")]
+ Decimal,
+ #[serde(deserialize_with = "de_unit_v::hexadecimal")]
+ Hexadecimal,
+ #[serde(deserialize_with = "de_unit_v::both")]
+ Both,
+}
+
macro_rules! _config_data {
(struct $name:ident {
$(
@@ -1940,7 +2051,7 @@ fn get_field<T: DeserializeOwned>(
alias: Option<&'static str>,
default: &str,
) -> T {
- // XXX: check alias first, to work-around the VS Code where it pre-fills the
+ // XXX: check alias first, to work around the VS Code where it pre-fills the
// defaults instead of sending an empty object.
alias
.into_iter()
@@ -1960,7 +2071,9 @@ fn get_field<T: DeserializeOwned>(
None
}
})
- .unwrap_or_else(|| serde_json::from_str(default).unwrap())
+ .unwrap_or_else(|| {
+ serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`"))
+ })
}
fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
@@ -2020,6 +2133,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"FxHashMap<String, String>" => set! {
"type": "object",
},
+ "FxHashMap<Box<str>, usize>" => set! {
+ "type": "object",
+ },
"Option<usize>" => set! {
"type": ["null", "integer"],
"minimum": 0,
@@ -2169,8 +2285,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"enumDescriptions": [
"Always show adjustment hints as prefix (`*expr`).",
"Always show adjustment hints as postfix (`expr.*`).",
- "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.",
- "Show prefix or postfix depending on which uses less parenthesis, prefering postfix.",
+ "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.",
+ "Show prefix or postfix depending on which uses less parenthesis, preferring postfix.",
]
},
"CargoFeaturesDef" => set! {
@@ -2275,6 +2391,32 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
},
],
},
+ "ClosureStyle" => set! {
+ "type": "string",
+ "enum": ["impl_fn", "rust_analyzer", "with_id", "hide"],
+ "enumDescriptions": [
+ "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
+ "`rust_analyzer`: `|i32, u64| -> i8`",
+ "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
+ "`hide`: Shows `...` for every closure type",
+ ],
+ },
+ "Option<MemoryLayoutHoverRenderKindDef>" => set! {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "string",
+ "enum": ["both", "decimal", "hexadecimal", ],
+ "enumDescriptions": [
+ "Render as 12 (0xC)",
+ "Render as 12",
+ "Render as 0xC"
+ ],
+ },
+ ],
+ },
_ => panic!("missing entry for {ty}: {default}"),
}
@@ -2384,4 +2526,43 @@ mod tests {
fn remove_ws(text: &str) -> String {
text.replace(char::is_whitespace, "")
}
+
+ #[test]
+ fn proc_macro_srv_null() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro_server": null,
+ }))
+ .unwrap();
+ assert_eq!(config.proc_macro_srv(), None);
+ }
+
+ #[test]
+ fn proc_macro_srv_abs() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro": {"server": project_root().display().to_string()}
+ }))
+ .unwrap();
+ assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap()));
+ }
+
+ #[test]
+ fn proc_macro_srv_rel() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro": {"server": "./server"}
+ }))
+ .unwrap();
+ assert_eq!(
+ config.proc_macro_srv(),
+ Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap())
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index 83b03fe47..33422fd05 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -1,15 +1,16 @@
//! Book keeping for keeping diagnostics easily in sync with the client.
pub(crate) mod to_proto;
-use std::{mem, sync::Arc};
+use std::mem;
use ide::FileId;
use ide_db::FxHashMap;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use nohash_hasher::{IntMap, IntSet};
+use triomphe::Arc;
use crate::lsp_ext;
-pub(crate) type CheckFixes = Arc<NoHashHashMap<usize, NoHashHashMap<FileId, Vec<Fix>>>>;
+pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
#[derive(Debug, Default, Clone)]
pub struct DiagnosticsMapConfig {
@@ -20,12 +21,12 @@ pub struct DiagnosticsMapConfig {
#[derive(Debug, Default, Clone)]
pub(crate) struct DiagnosticCollection {
- // FIXME: should be NoHashHashMap<FileId, Vec<ra_id::Diagnostic>>
- pub(crate) native: NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>,
+ // FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>>
+ pub(crate) native: IntMap<FileId, Vec<lsp_types::Diagnostic>>,
// FIXME: should be Vec<flycheck::Diagnostic>
- pub(crate) check: NoHashHashMap<usize, NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
+ pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
pub(crate) check_fixes: CheckFixes,
- changes: NoHashHashSet<FileId>,
+ changes: IntSet<FileId>,
}
#[derive(Debug, Clone)]
@@ -105,7 +106,7 @@ impl DiagnosticCollection {
native.chain(check)
}
- pub(crate) fn take_changes(&mut self) -> Option<NoHashHashSet<FileId>> {
+ pub(crate) fn take_changes(&mut self) -> Option<IntSet<FileId>> {
if self.changes.is_empty() {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 415fa4e02..e1d1130ff 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -3,7 +3,6 @@
use std::collections::HashMap;
use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
-use ide_db::line_index::WideEncoding;
use itertools::Itertools;
use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
@@ -80,37 +79,33 @@ fn position(
position_encoding: &PositionEncoding,
span: &DiagnosticSpan,
line_offset: usize,
- column_offset: usize,
+ column_offset_utf32: usize,
) -> lsp_types::Position {
let line_index = line_offset - span.line_start;
- let mut true_column_offset = column_offset;
- if let Some(line) = span.text.get(line_index) {
- if line.text.chars().count() == line.text.len() {
- // all one byte utf-8 char
- return lsp_types::Position {
- line: (line_offset as u32).saturating_sub(1),
- character: (column_offset as u32).saturating_sub(1),
- };
- }
- let mut char_offset = 0;
- let len_func = match position_encoding {
- PositionEncoding::Utf8 => char::len_utf8,
- PositionEncoding::Wide(WideEncoding::Utf16) => char::len_utf16,
- PositionEncoding::Wide(WideEncoding::Utf32) => |_| 1,
- };
- for c in line.text.chars() {
- char_offset += 1;
- if char_offset > column_offset {
- break;
+ let column_offset_encoded = match span.text.get(line_index) {
+ // Fast path.
+ Some(line) if line.text.is_ascii() => column_offset_utf32,
+ Some(line) => {
+ let line_prefix_len = line
+ .text
+ .char_indices()
+ .take(column_offset_utf32)
+ .last()
+ .map(|(pos, c)| pos + c.len_utf8())
+ .unwrap_or(0);
+ let line_prefix = &line.text[..line_prefix_len];
+ match position_encoding {
+ PositionEncoding::Utf8 => line_prefix.len(),
+ PositionEncoding::Wide(enc) => enc.measure(line_prefix),
}
- true_column_offset += len_func(c) - 1;
}
- }
+ None => column_offset_utf32,
+ };
lsp_types::Position {
line: (line_offset as u32).saturating_sub(1),
- character: (true_column_offset as u32).saturating_sub(1),
+ character: (column_offset_encoded as u32).saturating_sub(1),
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
index 313bb2ec8..4e57c6eb6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -4,6 +4,7 @@ use std::{fmt, panic, thread};
use ide::Cancelled;
use lsp_server::ExtractError;
use serde::{de::DeserializeOwned, Serialize};
+use stdx::thread::ThreadIntent;
use crate::{
global_state::{GlobalState, GlobalStateSnapshot},
@@ -87,7 +88,8 @@ impl<'a> RequestDispatcher<'a> {
self
}
- /// Dispatches the request onto thread pool
+ /// Dispatches a non-latency-sensitive request onto the thread pool
+ /// without retrying it if it panics.
pub(crate) fn on_no_retry<R>(
&mut self,
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -102,7 +104,7 @@ impl<'a> RequestDispatcher<'a> {
None => return self,
};
- self.global_state.task_pool.handle.spawn({
+ self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, {
let world = self.global_state.snapshot();
move || {
let result = panic::catch_unwind(move || {
@@ -123,7 +125,7 @@ impl<'a> RequestDispatcher<'a> {
self
}
- /// Dispatches the request onto thread pool
+ /// Dispatches a non-latency-sensitive request onto the thread pool.
pub(crate) fn on<R>(
&mut self,
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -133,26 +135,35 @@ impl<'a> RequestDispatcher<'a> {
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
R::Result: Serialize,
{
- let (req, params, panic_context) = match self.parse::<R>() {
- Some(it) => it,
- None => return self,
- };
+ self.on_with_thread_intent::<true, R>(ThreadIntent::Worker, f)
+ }
- self.global_state.task_pool.handle.spawn({
- let world = self.global_state.snapshot();
- move || {
- let result = panic::catch_unwind(move || {
- let _pctx = stdx::panic_context::enter(panic_context);
- f(world, params)
- });
- match thread_result_to_response::<R>(req.id.clone(), result) {
- Ok(response) => Task::Response(response),
- Err(_) => Task::Retry(req),
- }
- }
- });
+ /// Dispatches a latency-sensitive request onto the thread pool.
+ pub(crate) fn on_latency_sensitive<R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ self.on_with_thread_intent::<true, R>(ThreadIntent::LatencySensitive, f)
+ }
- self
+ /// Formatting requests should never block on waiting a for task thread to open up, editors will wait
+ /// on the response and a late formatting update might mess with the document and user.
+ /// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete!
+ pub(crate) fn on_fmt_thread<R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ self.on_with_thread_intent::<false, R>(ThreadIntent::LatencySensitive, f)
}
pub(crate) fn finish(&mut self) {
@@ -167,6 +178,41 @@ impl<'a> RequestDispatcher<'a> {
}
}
+ fn on_with_thread_intent<const MAIN_POOL: bool, R>(
+ &mut self,
+ intent: ThreadIntent,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ let (req, params, panic_context) = match self.parse::<R>() {
+ Some(it) => it,
+ None => return self,
+ };
+
+ let world = self.global_state.snapshot();
+ if MAIN_POOL {
+ &mut self.global_state.task_pool.handle
+ } else {
+ &mut self.global_state.fmt_pool.handle
+ }
+ .spawn(intent, move || {
+ let result = panic::catch_unwind(move || {
+ let _pctx = stdx::panic_context::enter(panic_context);
+ f(world, params)
+ });
+ match thread_result_to_response::<R>(req.id.clone(), result) {
+ Ok(response) => Task::Response(response),
+ Err(_) => Task::Retry(req),
+ }
+ });
+
+ self
+ }
+
fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
where
R: lsp_types::request::Request,
@@ -261,7 +307,7 @@ pub(crate) struct NotificationDispatcher<'a> {
}
impl<'a> NotificationDispatcher<'a> {
- pub(crate) fn on<N>(
+ pub(crate) fn on_sync_mut<N>(
&mut self,
f: fn(&mut GlobalState, N::Params) -> Result<()>,
) -> Result<&mut Self>
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
index 50af38cd6..cd74a5500 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
@@ -31,7 +31,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R
PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character },
PositionEncoding::Wide(enc) => {
let line_col = WideLineCol { line: position.line, col: position.character };
- line_index.index.to_utf8(enc, line_col)
+ line_index
+ .index
+ .to_utf8(enc, line_col)
+ .ok_or_else(|| format_err!("Invalid wide col offset"))?
}
};
let text_size =
@@ -98,13 +101,18 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
pub(crate) fn annotation(
snap: &GlobalStateSnapshot,
code_lens: lsp_types::CodeLens,
-) -> Result<Annotation> {
+) -> Result<Option<Annotation>> {
let data =
code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?;
let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", &data)?;
- match resolve {
- lsp_ext::CodeLensResolveData::Impls(params) => {
+ match resolve.kind {
+ lsp_ext::CodeLensResolveDataKind::Impls(params) => {
+ if snap.url_file_version(&params.text_document_position_params.text_document.uri)
+ != Some(resolve.version)
+ {
+ return Ok(None);
+ }
let pos @ FilePosition { file_id, .. } =
file_position(snap, params.text_document_position_params)?;
let line_index = snap.file_line_index(file_id)?;
@@ -114,7 +122,10 @@ pub(crate) fn annotation(
kind: AnnotationKind::HasImpls { pos, data: None },
})
}
- lsp_ext::CodeLensResolveData::References(params) => {
+ lsp_ext::CodeLensResolveDataKind::References(params) => {
+ if snap.url_file_version(&params.text_document.uri) != Some(resolve.version) {
+ return Ok(None);
+ }
let pos @ FilePosition { file_id, .. } = file_position(snap, params)?;
let line_index = snap.file_line_index(file_id)?;
@@ -124,4 +135,5 @@ pub(crate) fn annotation(
})
}
}
+ .map(Some)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index aca6c9235..d5b0e3a57 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -3,22 +3,23 @@
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
-use std::{sync::Arc, time::Instant};
+use std::time::Instant;
use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
-use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
+use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
+use nohash_hasher::IntMap;
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
-use rustc_hash::FxHashMap;
-use stdx::hash::NoHashHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
+use triomphe::Arc;
use vfs::AnchoredPathBuf;
use crate::{
- config::Config,
+ config::{Config, ConfigError},
diagnostics::{CheckFixes, DiagnosticCollection},
from_proto,
line_index::{LineEndings, LineIndex},
@@ -51,24 +52,35 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
+
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
- pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
+ pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
+
pub(crate) config: Arc<Config>,
+ pub(crate) config_errors: Option<ConfigError>,
pub(crate) analysis_host: AnalysisHost,
pub(crate) diagnostics: DiagnosticCollection,
pub(crate) mem_docs: MemDocs,
+ pub(crate) source_root_config: SourceRootConfig,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
+
+ // status
pub(crate) shutdown_requested: bool,
- pub(crate) proc_macro_changed: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
- pub(crate) source_root_config: SourceRootConfig,
- pub(crate) proc_macro_clients: Vec<Result<ProcMacroServer, String>>,
+ // proc macros
+ pub(crate) proc_macro_changed: bool,
+ pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>,
+
+ // Flycheck
pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
+ pub(crate) last_flycheck_error: Option<String>,
- pub(crate) vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
+ // VFS
+ pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
+ pub(crate) vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
pub(crate) vfs_config_version: u32,
pub(crate) vfs_progress_config_version: u32,
pub(crate) vfs_progress_n_total: usize,
@@ -92,7 +104,7 @@ pub(crate) struct GlobalState {
/// first phase is much faster, and is much less likely to fail.
///
/// This creates a complication -- by the time the second phase completes,
- /// the results of the fist phase could be invalid. That is, while we run
+ /// the results of the first phase could be invalid. That is, while we run
/// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
/// `cargo metadata` completes before `cargo check`.
///
@@ -100,11 +112,15 @@ pub(crate) struct GlobalState {
/// the user just adds comments or whitespace to Cargo.toml, we do not want
/// to invalidate any salsa caches.
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
- pub(crate) fetch_workspaces_queue: OpQueue<Option<Vec<anyhow::Result<ProjectWorkspace>>>>,
- pub(crate) fetch_build_data_queue:
- OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
+ pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
- pub(crate) prime_caches_queue: OpQueue<()>,
+ // op queues
+ pub(crate) fetch_workspaces_queue:
+ OpQueue<bool, Option<(Vec<anyhow::Result<ProjectWorkspace>>, bool)>>,
+ pub(crate) fetch_build_data_queue:
+ OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
+ pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
+ pub(crate) prime_caches_queue: OpQueue,
}
/// An immutable snapshot of the world's state at a point in time.
@@ -114,8 +130,9 @@ pub(crate) struct GlobalStateSnapshot {
pub(crate) check_fixes: CheckFixes,
mem_docs: MemDocs,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
- vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
+ vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
+ // used to signal semantic highlighting to fall back to syntax based highlighting until proc-macros have been loaded
pub(crate) proc_macros_loaded: bool,
pub(crate) flycheck: Arc<[FlycheckHandle]>,
}
@@ -137,13 +154,22 @@ impl GlobalState {
let handle = TaskPool::new_with_threads(sender, config.main_loop_num_threads());
Handle { handle, receiver }
};
+ let fmt_pool = {
+ let (sender, receiver) = unbounded();
+ let handle = TaskPool::new_with_threads(sender, 1);
+ Handle { handle, receiver }
+ };
- let analysis_host = AnalysisHost::new(config.lru_capacity());
+ let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
+ if let Some(capacities) = config.lru_query_capacities() {
+ analysis_host.update_lru_capacities(capacities);
+ }
let (flycheck_sender, flycheck_receiver) = unbounded();
let mut this = GlobalState {
sender,
req_queue: ReqQueue::default(),
task_pool,
+ fmt_pool,
loader,
config: Arc::new(config.clone()),
analysis_host,
@@ -151,26 +177,33 @@ impl GlobalState {
mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false,
- proc_macro_changed: false,
last_reported_status: None,
source_root_config: SourceRootConfig::default(),
- proc_macro_clients: vec![],
+ config_errors: Default::default(),
+
+ proc_macro_changed: false,
+ // FIXME: use `Arc::from_iter` when it becomes available
+ proc_macro_clients: Arc::from(Vec::new()),
- flycheck: Arc::new([]),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ flycheck: Arc::from(Vec::new()),
flycheck_sender,
flycheck_receiver,
+ last_flycheck_error: None,
- vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))),
+ vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
vfs_config_version: 0,
vfs_progress_config_version: 0,
vfs_progress_n_total: 0,
vfs_progress_n_done: 0,
workspaces: Arc::new(Vec::new()),
+ crate_graph_file_dependencies: FxHashSet::default(),
fetch_workspaces_queue: OpQueue::default(),
- prime_caches_queue: OpQueue::default(),
-
fetch_build_data_queue: OpQueue::default(),
+ fetch_proc_macros_queue: OpQueue::default(),
+
+ prime_caches_queue: OpQueue::default(),
};
// Apply any required database inputs from the config.
this.update_configuration(config);
@@ -179,13 +212,12 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes");
- let mut workspace_structure_change = None;
let mut file_changes = FxHashMap::default();
- let (change, changed_files) = {
+ let (change, changed_files, workspace_structure_change) = {
let mut change = Change::new();
let (vfs, line_endings_map) = &mut *self.vfs.write();
- let mut changed_files = vfs.take_changes();
+ let changed_files = vfs.take_changes();
if changed_files.is_empty() {
return false;
}
@@ -193,7 +225,7 @@ impl GlobalState {
// We need to fix up the changed events a bit. If we have a create or modify for a file
// id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on.
- for changed_file in &changed_files {
+ for changed_file in changed_files {
use vfs::ChangeKind::*;
file_changes
@@ -229,25 +261,28 @@ impl GlobalState {
));
}
- changed_files.extend(
- file_changes
- .into_iter()
- .filter(|(_, (change_kind, just_created))| {
- !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
- })
- .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }),
- );
+ let changed_files: Vec<_> = file_changes
+ .into_iter()
+ .filter(|(_, (change_kind, just_created))| {
+ !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
+ })
+ .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind })
+ .collect();
+ let mut workspace_structure_change = None;
// A file was added or deleted
let mut has_structure_changes = false;
for file in &changed_files {
- if let Some(path) = vfs.file_path(file.file_id).as_path() {
+ let vfs_path = &vfs.file_path(file.file_id);
+ if let Some(path) = vfs_path.as_path() {
let path = path.to_path_buf();
if reload::should_refresh_for_change(&path, file.change_kind) {
- workspace_structure_change = Some(path);
+ workspace_structure_change = Some((path.clone(), false));
}
if file.is_created_or_deleted() {
has_structure_changes = true;
+ workspace_structure_change =
+ Some((path, self.crate_graph_file_dependencies.contains(vfs_path)));
}
}
@@ -261,7 +296,7 @@ impl GlobalState {
String::from_utf8(bytes).ok().and_then(|text| {
let (text, line_endings) = LineEndings::normalize(text);
line_endings_map.insert(file.file_id, line_endings);
- Some(Arc::new(text))
+ Some(Arc::from(text))
})
} else {
None
@@ -272,7 +307,7 @@ impl GlobalState {
let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
}
- (change, changed_files)
+ (change, changed_files, workspace_structure_change)
};
self.analysis_host.apply_change(change);
@@ -280,11 +315,13 @@ impl GlobalState {
{
let raw_database = self.analysis_host.raw_database();
// FIXME: ideally we should only trigger a workspace fetch for non-library changes
- // but somethings going wrong with the source root business when we add a new local
+ // but something's going wrong with the source root business when we add a new local
// crate see https://github.com/rust-lang/rust-analyzer/issues/13029
- if let Some(path) = workspace_structure_change {
- self.fetch_workspaces_queue
- .request_op(format!("workspace vfs file change: {}", path.display()));
+ if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
+ self.fetch_workspaces_queue.request_op(
+ format!("workspace vfs file change: {}", path.display()),
+ force_crate_graph_reload,
+ );
}
self.proc_macro_changed =
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
@@ -307,7 +344,8 @@ impl GlobalState {
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
- proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(),
+ proc_macros_loaded: !self.config.expand_proc_macros()
+ || *self.fetch_proc_macros_queue.last_op_result(),
flycheck: self.flycheck.clone(),
}
}
@@ -331,7 +369,7 @@ impl GlobalState {
}
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
- &mut self,
+ &self,
params: N::Params,
) {
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
@@ -372,7 +410,7 @@ impl GlobalState {
self.req_queue.incoming.is_completed(&request.id)
}
- fn send(&mut self, message: lsp_server::Message) {
+ fn send(&self, message: lsp_server::Message) {
self.sender.send(message).unwrap()
}
}
@@ -431,6 +469,10 @@ impl GlobalStateSnapshot {
ProjectWorkspace::DetachedFiles { .. } => None,
})
}
+
+ pub(crate) fn vfs_memory_usage(&self) -> usize {
+ self.vfs.read().0.memory_usage()
+ }
}
pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
new file mode 100644
index 000000000..ae1dc2315
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -0,0 +1,334 @@
+//! This module is responsible for implementing handlers for Language Server
+//! Protocol. This module specifically handles notifications.
+
+use std::ops::Deref;
+
+use itertools::Itertools;
+use lsp_types::{
+ CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams,
+ DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
+ DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams,
+};
+use triomphe::Arc;
+use vfs::{AbsPathBuf, ChangeKind, VfsPath};
+
+use crate::{
+ config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams,
+ lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, Result,
+};
+
+pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> {
+ let id: lsp_server::RequestId = match params.id {
+ lsp_types::NumberOrString::Number(id) => id.into(),
+ lsp_types::NumberOrString::String(id) => id.into(),
+ };
+ state.cancel(id);
+ Ok(())
+}
+
+pub(crate) fn handle_work_done_progress_cancel(
+ state: &mut GlobalState,
+ params: WorkDoneProgressCancelParams,
+) -> Result<()> {
+ if let lsp_types::NumberOrString::String(s) = &params.token {
+ if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") {
+ if let Ok(id) = u32::from_str_radix(id, 10) {
+ if let Some(flycheck) = state.flycheck.get(id as usize) {
+ flycheck.cancel();
+ }
+ }
+ }
+ }
+
+ // Just ignore this. It is OK to continue sending progress
+ // notifications for this token, as the client can't know when
+ // we accepted notification.
+ Ok(())
+}
+
+pub(crate) fn handle_did_open_text_document(
+ state: &mut GlobalState,
+ params: DidOpenTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_open_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ let already_exists = state
+ .mem_docs
+ .insert(path.clone(), DocumentData::new(params.text_document.version))
+ .is_err();
+ if already_exists {
+ tracing::error!("duplicate DidOpenTextDocument: {}", path);
+ }
+ state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_text_document(
+ state: &mut GlobalState,
+ params: DidChangeTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_change_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ match state.mem_docs.get_mut(&path) {
+ Some(doc) => {
+ // The version passed in DidChangeTextDocument is the version after all edits are applied
+ // so we should apply it before the vfs is notified.
+ doc.version = params.text_document.version;
+ }
+ None => {
+ tracing::error!("unexpected DidChangeTextDocument: {}", path);
+ return Ok(());
+ }
+ };
+
+ let vfs = &mut state.vfs.write().0;
+ let file_id = vfs.file_id(&path).unwrap();
+ let text = apply_document_changes(
+ state.config.position_encoding(),
+ || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
+ params.content_changes,
+ );
+
+ vfs.set_file_contents(path, Some(text.into_bytes()));
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_close_text_document(
+ state: &mut GlobalState,
+ params: DidCloseTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_close_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ if state.mem_docs.remove(&path).is_err() {
+ tracing::error!("orphan DidCloseTextDocument: {}", path);
+ }
+
+ state.semantic_tokens_cache.lock().remove(&params.text_document.uri);
+
+ if let Some(path) = path.as_path() {
+ state.loader.handle.invalidate(path.to_path_buf());
+ }
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_save_text_document(
+ state: &mut GlobalState,
+ params: DidSaveTextDocumentParams,
+) -> Result<()> {
+ if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
+ // Re-fetch workspaces if a workspace related file has changed
+ if let Some(abs_path) = vfs_path.as_path() {
+ if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
+ state
+ .fetch_workspaces_queue
+ .request_op(format!("DidSaveTextDocument {}", abs_path.display()), false);
+ }
+ }
+
+ if !state.config.check_on_save() || run_flycheck(state, vfs_path) {
+ return Ok(());
+ }
+ } else if state.config.check_on_save() {
+ // No specific flycheck was triggered, so let's trigger all of them.
+ for flycheck in state.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_configuration(
+ state: &mut GlobalState,
+ _params: DidChangeConfigurationParams,
+) -> Result<()> {
+ // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
+ // this notification's parameters should be ignored and the actual config queried separately.
+ state.send_request::<lsp_types::request::WorkspaceConfiguration>(
+ lsp_types::ConfigurationParams {
+ items: vec![lsp_types::ConfigurationItem {
+ scope_uri: None,
+ section: Some("rust-analyzer".to_string()),
+ }],
+ },
+ |this, resp| {
+ tracing::debug!("config update response: '{:?}", resp);
+ let lsp_server::Response { error, result, .. } = resp;
+
+ match (error, result) {
+ (Some(err), _) => {
+ tracing::error!("failed to fetch the server settings: {:?}", err)
+ }
+ (None, Some(mut configs)) => {
+ if let Some(json) = configs.get_mut(0) {
+ // Note that json can be null according to the spec if the client can't
+ // provide a configuration. This is handled in Config::update below.
+ let mut config = Config::clone(&*this.config);
+ this.config_errors = config.update(json.take()).err();
+ this.update_configuration(config);
+ }
+ }
+ (None, None) => {
+ tracing::error!("received empty server settings response from the client")
+ }
+ }
+ },
+ );
+
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_workspace_folders(
+ state: &mut GlobalState,
+ params: DidChangeWorkspaceFoldersParams,
+) -> Result<()> {
+ let config = Arc::make_mut(&mut state.config);
+
+ for workspace in params.event.removed {
+ let Ok(path) = workspace.uri.to_file_path() else { continue };
+ let Ok(path) = AbsPathBuf::try_from(path) else { continue };
+ config.remove_workspace(&path);
+ }
+
+ let added = params
+ .event
+ .added
+ .into_iter()
+ .filter_map(|it| it.uri.to_file_path().ok())
+ .filter_map(|it| AbsPathBuf::try_from(it).ok());
+ config.add_workspaces(added);
+
+ if !config.has_linked_projects() && config.detached_files().is_empty() {
+ config.rediscover_workspaces();
+ state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), false)
+ }
+
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_watched_files(
+ state: &mut GlobalState,
+ params: DidChangeWatchedFilesParams,
+) -> Result<()> {
+ for change in params.changes {
+ if let Ok(path) = from_proto::abs_path(&change.uri) {
+ state.loader.handle.invalidate(path);
+ }
+ }
+ Ok(())
+}
+
+fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
+ let _p = profile::span("run_flycheck");
+
+ let file_id = state.vfs.read().0.file_id(&vfs_path);
+ if let Some(file_id) = file_id {
+ let world = state.snapshot();
+ let mut updated = false;
+ let task = move || -> std::result::Result<(), ide::Cancelled> {
+ // Trigger flychecks for all workspaces that depend on the saved file
+ // Crates containing or depending on the saved file
+ let crate_ids: Vec<_> = world
+ .analysis
+ .crates_for(file_id)?
+ .into_iter()
+ .flat_map(|id| world.analysis.transitive_rev_deps(id))
+ .flatten()
+ .sorted()
+ .unique()
+ .collect();
+
+ let crate_root_paths: Vec<_> = crate_ids
+ .iter()
+ .filter_map(|&crate_id| {
+ world
+ .analysis
+ .crate_root(crate_id)
+ .map(|file_id| {
+ world.file_id_to_file_path(file_id).as_path().map(ToOwned::to_owned)
+ })
+ .transpose()
+ })
+ .collect::<ide::Cancellable<_>>()?;
+ let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect();
+
+ // Find all workspaces that have at least one target containing the saved file
+ let workspace_ids = world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
+ project_model::ProjectWorkspace::Cargo { cargo, .. } => {
+ cargo.packages().any(|pkg| {
+ cargo[pkg]
+ .targets
+ .iter()
+ .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path()))
+ })
+ }
+ project_model::ProjectWorkspace::Json { project, .. } => {
+ project.crates().any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c))
+ }
+ project_model::ProjectWorkspace::DetachedFiles { .. } => false,
+ });
+
+ // Find and trigger corresponding flychecks
+ for flycheck in world.flycheck.iter() {
+ for (id, _) in workspace_ids.clone() {
+ if id == flycheck.id() {
+ updated = true;
+ flycheck.restart();
+ continue;
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ if !updated {
+ for flycheck in world.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+ };
+ state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| {
+ if let Err(e) = std::panic::catch_unwind(task) {
+ tracing::error!("flycheck task panicked: {e:?}")
+ }
+ });
+ true
+ } else {
+ false
+ }
+}
+
+pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
+ let _p = profile::span("handle_stop_flycheck");
+ state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
+ Ok(())
+}
+
+pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
+ let _p = profile::span("handle_clear_flycheck");
+ state.diagnostics.clear_check_all();
+ Ok(())
+}
+
+pub(crate) fn handle_run_flycheck(
+ state: &mut GlobalState,
+ params: RunFlycheckParams,
+) -> Result<()> {
+ let _p = profile::span("handle_run_flycheck");
+ if let Some(text_document) = params.text_document {
+ if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
+ if run_flycheck(state, vfs_path) {
+ return Ok(());
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ for flycheck in state.flycheck.iter() {
+ flycheck.restart();
+ }
+ Ok(())
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 2fca2ab85..a6a72552d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -1,26 +1,25 @@
//! This module is responsible for implementing handlers for Language Server
-//! Protocol. The majority of requests are fulfilled by calling into the
-//! `ide` crate.
+//! Protocol. This module specifically handles requests.
use std::{
+ fs,
io::Write as _,
process::{self, Stdio},
};
use anyhow::Context;
use ide::{
- AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition,
- FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable,
- RunnableKind, SingleResolve, SourceChange, TextEdit,
+ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
+ HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
+ SingleResolve, SourceChange, TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
use lsp_types::{
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
- CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
- FoldingRangeParams, HoverContents, InlayHint, InlayHintParams, Location, LocationLink,
- NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
+ CodeLens, CompletionItem, FoldingRange, FoldingRangeParams, HoverContents, InlayHint,
+ InlayHintParams, Location, LocationLink, Position, PrepareRenameResponse, Range, RenameParams,
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
@@ -29,7 +28,8 @@ use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
use serde_json::json;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
-use vfs::{AbsPath, AbsPathBuf};
+use triomphe::Arc;
+use vfs::{AbsPath, AbsPathBuf, VfsPath};
use crate::{
cargo_target_spec::CargoTargetSpec,
@@ -38,23 +38,29 @@ use crate::{
from_proto,
global_state::{GlobalState, GlobalStateSnapshot},
line_index::LineEndings,
- lsp_ext::{self, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams},
+ lsp_ext::{
+ self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
+ FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
+ },
lsp_utils::{all_edits_are_disjoint, invalid_params_error},
to_proto, LspError, Result,
};
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
- state.proc_macro_clients.clear();
+ // FIXME: use `Arc::from_iter` when it becomes available
+ state.proc_macro_clients = Arc::from(Vec::new());
state.proc_macro_changed = false;
- state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
- state.fetch_build_data_queue.request_op("reload workspace request".to_string());
+ state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), false);
Ok(())
}
-pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
- let _p = profile::span("handle_stop_flycheck");
- state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
+pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> {
+ // FIXME: use `Arc::from_iter` when it becomes available
+ state.proc_macro_clients = Arc::from(Vec::new());
+ state.proc_macro_changed = false;
+
+ state.fetch_build_data_queue.request_op("rebuild proc macros request".to_string(), ());
Ok(())
}
@@ -95,6 +101,7 @@ pub(crate) fn handle_analyzer_status(
.collect::<Vec<&AbsPath>>()
);
}
+ format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _));
buf.push_str("\nAnalysis:\n");
buf.push_str(
&snap
@@ -107,13 +114,14 @@ pub(crate) fn handle_analyzer_status(
pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
let _p = profile::span("handle_memory_usage");
- let mut mem = state.analysis_host.per_query_memory_usage();
- mem.push(("Remaining".into(), profile::memory_usage().allocated));
+ let mem = state.analysis_host.per_query_memory_usage();
let mut out = String::new();
- for (name, bytes) in mem {
- format_to!(out, "{:>8} {}\n", bytes, name);
+ for (name, bytes, entries) in mem {
+ format_to!(out, "{:>8} {:>6} {}\n", bytes, entries, name);
}
+ format_to!(out, "{:>8} Remaining\n", profile::memory_usage().allocated);
+
Ok(out)
}
@@ -154,6 +162,16 @@ pub(crate) fn handle_view_mir(
Ok(res)
}
+pub(crate) fn handle_interpret_function(
+ snap: GlobalStateSnapshot,
+ params: lsp_types::TextDocumentPositionParams,
+) -> Result<String> {
+ let _p = profile::span("handle_interpret_function");
+ let position = from_proto::file_position(&snap, params)?;
+ let res = snap.analysis.interpret_function(position)?;
+ Ok(res)
+}
+
pub(crate) fn handle_view_file_text(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentIdentifier,
@@ -502,7 +520,10 @@ pub(crate) fn handle_workspace_symbol(
#[allow(deprecated)]
let info = SymbolInformation {
- name: nav.name.to_string(),
+ name: match &nav.alias {
+ Some(alias) => format!("{} (alias for {})", alias, nav.name),
+ None => format!("{}", nav.name),
+ },
kind: nav
.kind
.map(to_proto::symbol_kind)
@@ -747,20 +768,25 @@ pub(crate) fn handle_runnables(
let config = snap.config.runnables();
match cargo_spec {
Some(spec) => {
+ let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?;
for cmd in ["check", "test"] {
+ let mut cargo_args =
+ vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()];
+ if all_targets {
+ cargo_args.push("--all-targets".to_owned());
+ }
res.push(lsp_ext::Runnable {
- label: format!("cargo {cmd} -p {} --all-targets", spec.package),
+ label: format!(
+ "cargo {cmd} -p {}{all_targets}",
+ spec.package,
+ all_targets = if all_targets { " --all-targets" } else { "" }
+ ),
location: None,
kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::CargoRunnable {
workspace_root: Some(spec.workspace_root.clone().into()),
override_cargo: config.override_cargo.clone(),
- cargo_args: vec![
- cmd.to_string(),
- "--package".to_string(),
- spec.package.clone(),
- "--all-targets".to_string(),
- ],
+ cargo_args,
cargo_extra_args: config.cargo_extra_args.clone(),
executable_args: Vec::new(),
expect_test: None,
@@ -769,14 +795,7 @@ pub(crate) fn handle_runnables(
}
}
None => {
- if !snap.config.linked_projects().is_empty()
- || !snap
- .config
- .discovered_projects
- .as_ref()
- .map(|projects| projects.is_empty())
- .unwrap_or(true)
- {
+ if !snap.config.linked_projects().is_empty() {
res.push(lsp_ext::Runnable {
label: "cargo check --workspace".to_string(),
location: None,
@@ -1057,7 +1076,7 @@ pub(crate) fn handle_references(
pub(crate) fn handle_formatting(
snap: GlobalStateSnapshot,
- params: DocumentFormattingParams,
+ params: lsp_types::DocumentFormattingParams,
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
let _p = profile::span("handle_formatting");
@@ -1262,7 +1281,7 @@ pub(crate) fn handle_code_lens_resolve(
snap: GlobalStateSnapshot,
code_lens: CodeLens,
) -> Result<CodeLens> {
- let annotation = from_proto::annotation(&snap, code_lens.clone())?;
+ let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) };
let annotation = snap.analysis.resolve_annotation(annotation)?;
let mut acc = Vec::new();
@@ -1321,38 +1340,6 @@ pub(crate) fn handle_ssr(
to_proto::workspace_edit(&snap, source_change).map_err(Into::into)
}
-pub(crate) fn publish_diagnostics(
- snap: &GlobalStateSnapshot,
- file_id: FileId,
-) -> Result<Vec<Diagnostic>> {
- let _p = profile::span("publish_diagnostics");
- let line_index = snap.file_line_index(file_id)?;
-
- let diagnostics: Vec<Diagnostic> = snap
- .analysis
- .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)?
- .into_iter()
- .map(|d| Diagnostic {
- range: to_proto::range(&line_index, d.range),
- severity: Some(to_proto::diagnostic_severity(d.severity)),
- code: Some(NumberOrString::String(d.code.as_str().to_string())),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&format!(
- "https://rust-analyzer.github.io/manual.html#{}",
- d.code.as_str()
- ))
- .unwrap(),
- }),
- source: Some("rust-analyzer".to_string()),
- message: d.message,
- related_information: None,
- tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None },
- data: None,
- })
- .collect();
- Ok(diagnostics)
-}
-
pub(crate) fn handle_inlay_hints(
snap: GlobalStateSnapshot,
params: InlayHintParams,
@@ -1370,9 +1357,7 @@ pub(crate) fn handle_inlay_hints(
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
.into_iter()
- .map(|it| {
- to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
- })
+ .map(|it| to_proto::inlay_hint(&snap, &line_index, it))
.collect::<Cancellable<Vec<_>>>()?,
))
}
@@ -1493,7 +1478,13 @@ pub(crate) fn handle_semantic_tokens_full(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
// Unconditionally cache the tokens
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
@@ -1517,7 +1508,13 @@ pub(crate) fn handle_semantic_tokens_full_delta(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
let mut cache = snap.semantic_tokens_cache.lock();
let cached_tokens = cache.entry(params.text_document.uri).or_default();
@@ -1551,20 +1548,53 @@ pub(crate) fn handle_semantic_tokens_range(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight_range(highlight_config, frange)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
Ok(Some(semantic_tokens.into()))
}
pub(crate) fn handle_open_docs(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentPositionParams,
-) -> Result<Option<lsp_types::Url>> {
+) -> Result<ExternalDocsResponse> {
let _p = profile::span("handle_open_docs");
let position = from_proto::file_position(&snap, params)?;
- let remote = snap.analysis.external_docs(position)?;
+ let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws {
+ ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())),
+ ProjectWorkspace::Json { .. } => None,
+ ProjectWorkspace::DetachedFiles { .. } => None,
+ });
+
+ let (cargo, sysroot) = match ws_and_sysroot {
+ Some((ws, sysroot)) => (Some(ws), sysroot),
+ _ => (None, None),
+ };
+
+ let sysroot = sysroot.map(|p| p.root().as_os_str());
+ let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str());
+
+ let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else {
+ return if snap.config.local_docs() {
+ Ok(ExternalDocsResponse::WithLocal(Default::default()))
+ } else {
+ Ok(ExternalDocsResponse::Simple(None))
+ }
+ };
- Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
+ let web = remote_urls.web_url.and_then(|it| Url::parse(&it).ok());
+ let local = remote_urls.local_url.and_then(|it| Url::parse(&it).ok());
+
+ if snap.config.local_docs() {
+ Ok(ExternalDocsResponse::WithLocal(ExternalDocsPair { web, local }))
+ } else {
+ Ok(ExternalDocsResponse::Simple(web))
+ }
}
pub(crate) fn handle_open_cargo_toml(
@@ -1908,3 +1938,52 @@ fn run_rustfmt(
Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
}
}
+
+pub(crate) fn fetch_dependency_list(
+ state: GlobalStateSnapshot,
+ _params: FetchDependencyListParams,
+) -> Result<FetchDependencyListResult> {
+ let crates = state.analysis.fetch_crates()?;
+ let crate_infos = crates
+ .into_iter()
+ .filter_map(|it| {
+ let root_file_path = state.file_id_to_file_path(it.root_file_id);
+ crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult {
+ name: it.name,
+ version: it.version,
+ path,
+ })
+ })
+ .collect();
+ Ok(FetchDependencyListResult { crates: crate_infos })
+}
+
+/// Searches for the directory of a Rust crate given this crate's root file path.
+///
+/// # Arguments
+///
+/// * `root_file_path`: The path to the root file of the crate.
+///
+/// # Returns
+///
+/// An `Option` value representing the path to the directory of the crate with the given
+/// name, if such a crate is found. If no crate with the given name is found, this function
+/// returns `None`.
+fn crate_path(root_file_path: VfsPath) -> Option<VfsPath> {
+ let mut current_dir = root_file_path.parent();
+ while let Some(path) = current_dir {
+ let cargo_toml_path = path.join("../Cargo.toml")?;
+ if fs::metadata(cargo_toml_path.as_path()?).is_ok() {
+ let crate_path = cargo_toml_path.parent()?;
+ return Some(crate_path);
+ }
+ current_dir = path.parent();
+ }
+ None
+}
+
+fn to_url(path: VfsPath) -> Option<Url> {
+ let path = path.as_path()?;
+ let str_path = path.as_os_str().to_str()?;
+ Url::from_file_path(str_path).ok()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index e8912b907..bd9f471a4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -10,8 +10,6 @@
//! in release mode in VS Code. There's however "rust-analyzer: Copy Run Command Line"
//! which you can use to paste the command in terminal and add `--release` manually.
-use std::sync::Arc;
-
use ide::{CallableSnippets, Change, CompletionConfig, FilePosition, TextSize};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -19,6 +17,7 @@ use ide_db::{
};
use project_model::CargoConfig;
use test_utils::project_root;
+use triomphe::Arc;
use vfs::{AbsPathBuf, VfsPath};
use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -65,7 +64,7 @@ fn integrated_highlighting_benchmark() {
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
text.push_str("\npub fn _dummy() {}\n");
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
}
@@ -121,7 +120,7 @@ fn integrated_completion_benchmark() {
patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
+ "sel".len();
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
completion_offset
};
@@ -160,7 +159,7 @@ fn integrated_completion_benchmark() {
patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
+ "self.".len();
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
completion_offset
};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 32dc3750f..65de4366e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -25,7 +25,6 @@ mod diff;
mod dispatch;
mod from_proto;
mod global_state;
-mod handlers;
mod line_index;
mod lsp_utils;
mod main_loop;
@@ -38,6 +37,11 @@ mod task_pool;
mod to_proto;
mod version;
+mod handlers {
+ pub(crate) mod notification;
+ pub(crate) mod request;
+}
+
pub mod config;
pub mod lsp_ext;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
index 791cd931d..15450303f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
@@ -5,9 +5,8 @@
//! This module does line ending conversion and detection (so that we can
//! convert back to `\r\n` on the way out).
-use std::sync::Arc;
-
use ide_db::line_index::WideEncoding;
+use triomphe::Arc;
#[derive(Clone, Copy)]
pub enum PositionEncoding {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
index c7b513db9..4d67c8b30 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,11 +4,11 @@ use std::{collections::HashMap, path::PathBuf};
use ide_db::line_index::WideEncoding;
use lsp_types::request::Request;
-use lsp_types::PositionEncodingKind;
use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
};
+use lsp_types::{PositionEncodingKind, Url};
use serde::{Deserialize, Serialize};
use crate::line_index::PositionEncoding;
@@ -27,6 +27,31 @@ pub struct AnalyzerStatusParams {
pub text_document: Option<TextDocumentIdentifier>,
}
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct CrateInfoResult {
+ pub name: Option<String>,
+ pub version: Option<String>,
+ pub path: Url,
+}
+pub enum FetchDependencyList {}
+
+impl Request for FetchDependencyList {
+ type Params = FetchDependencyListParams;
+ type Result = FetchDependencyListResult;
+ const METHOD: &'static str = "rust-analyzer/fetchDependencyList";
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct FetchDependencyListParams {}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct FetchDependencyListResult {
+ pub crates: Vec<CrateInfoResult>,
+}
+
pub enum MemoryUsage {}
impl Request for MemoryUsage {
@@ -51,6 +76,14 @@ impl Request for ReloadWorkspace {
const METHOD: &'static str = "rust-analyzer/reloadWorkspace";
}
+pub enum RebuildProcMacros {}
+
+impl Request for RebuildProcMacros {
+ type Params = ();
+ type Result = ();
+ const METHOD: &'static str = "rust-analyzer/rebuildProcMacros";
+}
+
pub enum SyntaxTree {}
impl Request for SyntaxTree {
@@ -82,6 +115,14 @@ impl Request for ViewMir {
const METHOD: &'static str = "rust-analyzer/viewMir";
}
+pub enum InterpretFunction {}
+
+impl Request for InterpretFunction {
+ type Params = lsp_types::TextDocumentPositionParams;
+ type Result = String;
+ const METHOD: &'static str = "rust-analyzer/interpretFunction";
+}
+
pub enum ViewFileText {}
impl Request for ViewFileText {
@@ -343,6 +384,7 @@ impl Request for CodeActionRequest {
}
pub enum CodeActionResolveRequest {}
+
impl Request for CodeActionResolveRequest {
type Params = CodeAction;
type Result = CodeAction;
@@ -418,7 +460,7 @@ pub enum HoverRequest {}
impl Request for HoverRequest {
type Params = HoverParams;
type Result = Option<Hover>;
- const METHOD: &'static str = "textDocument/hover";
+ const METHOD: &'static str = lsp_types::request::HoverRequest::METHOD;
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -466,10 +508,24 @@ pub enum ExternalDocs {}
impl Request for ExternalDocs {
type Params = lsp_types::TextDocumentPositionParams;
- type Result = Option<lsp_types::Url>;
+ type Result = ExternalDocsResponse;
const METHOD: &'static str = "experimental/externalDocs";
}
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum ExternalDocsResponse {
+ Simple(Option<lsp_types::Url>),
+ WithLocal(ExternalDocsPair),
+}
+
+#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct ExternalDocsPair {
+ pub web: Option<lsp_types::Url>,
+ pub local: Option<lsp_types::Url>,
+}
+
pub enum OpenCargoToml {}
impl Request for OpenCargoToml {
@@ -487,7 +543,14 @@ pub struct OpenCargoTomlParams {
/// Information about CodeLens, that is to be resolved.
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
-pub(crate) enum CodeLensResolveData {
+pub struct CodeLensResolveData {
+ pub version: i32,
+ pub kind: CodeLensResolveDataKind,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum CodeLensResolveDataKind {
Impls(lsp_types::request::GotoImplementationParams),
References(lsp_types::TextDocumentPositionParams),
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
index 12e5caf2c..74e79e8e6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,8 +1,9 @@
//! Utilities for LSP-related boilerplate code.
-use std::{mem, ops::Range, sync::Arc};
+use std::{mem, ops::Range};
use lsp_server::Notification;
use lsp_types::request::Request;
+use triomphe::Arc;
use crate::{
from_proto,
@@ -80,39 +81,14 @@ impl GlobalState {
match additional_info {
Some(additional_info) => {
tracing::error!("{}:\n{}", &message, &additional_info);
- match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) {
- true => self.send_request::<lsp_types::request::ShowMessageRequest>(
- lsp_types::ShowMessageRequestParams {
- typ: lsp_types::MessageType::ERROR,
- message,
- actions: Some(vec![lsp_types::MessageActionItem {
- title: "Open server logs".to_owned(),
- properties: Default::default(),
- }]),
- },
- |this, resp| {
- let lsp_server::Response { error: None, result: Some(result), .. } = resp
- else { return };
- if let Ok(Some(_item)) = crate::from_json::<
- <lsp_types::request::ShowMessageRequest as lsp_types::request::Request>::Result,
- >(
- lsp_types::request::ShowMessageRequest::METHOD, &result
- ) {
- this.send_notification::<lsp_ext::OpenServerLogs>(());
- }
- },
- ),
- false => self.send_notification::<lsp_types::notification::ShowMessage>(
- lsp_types::ShowMessageParams {
- typ: lsp_types::MessageType::ERROR,
- message,
- },
- ),
- }
+ self.show_message(
+ lsp_types::MessageType::ERROR,
+ message,
+ tracing::enabled!(tracing::Level::ERROR),
+ );
}
None => {
tracing::error!("{}", &message);
-
self.send_notification::<lsp_types::notification::ShowMessage>(
lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 67a54cde6..02dd94e5f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -2,8 +2,6 @@
//! requests/replies and notifications back to the client.
use std::{
fmt,
- ops::Deref,
- sync::Arc,
time::{Duration, Instant},
};
@@ -11,20 +9,20 @@ use always_assert::always;
use crossbeam_channel::{select, Receiver};
use flycheck::FlycheckHandle;
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
-use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
-use vfs::{AbsPathBuf, ChangeKind, FileId};
+use stdx::thread::ThreadIntent;
+use triomphe::Arc;
+use vfs::FileId;
use crate::{
config::Config,
dispatch::{NotificationDispatcher, RequestDispatcher},
from_proto,
global_state::{file_id_to_url, url_to_file_id, GlobalState},
- handlers, lsp_ext,
- lsp_utils::{apply_document_changes, notification_is, Progress},
- mem_docs::DocumentData,
- reload::{self, BuildDataProgress, ProjectWorkspaceProgress},
+ lsp_ext,
+ lsp_utils::{notification_is, Progress},
+ reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
Result,
};
@@ -36,7 +34,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
// temporary bumped. This optimization backfires in our case: each time the
// `main_loop` schedules a task to run on a threadpool, the worker threads
// gets a higher priority, and (on a machine with fewer cores) displaces the
- // main loop! We work-around this by marking the main loop as a
+ // main loop! We work around this by marking the main loop as a
// higher-priority thread.
//
// https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities
@@ -68,6 +66,7 @@ pub(crate) enum Task {
PrimeCaches(PrimeCachesProgress),
FetchWorkspace(ProjectWorkspaceProgress),
FetchBuildData(BuildDataProgress),
+ LoadProcMacros(ProcMacroProgress),
}
#[derive(Debug)]
@@ -79,7 +78,7 @@ pub(crate) enum PrimeCachesProgress {
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter<'_>| {
+ let debug_non_verbose = |not: &Notification, f: &mut fmt::Formatter<'_>| {
f.debug_struct("Notification").field("method", &not.method).finish()
};
@@ -88,7 +87,7 @@ impl fmt::Debug for Event {
if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
|| notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
{
- return debug_verbose_not(not, f);
+ return debug_non_verbose(not, f);
}
}
Event::Task(Task::Response(resp)) => {
@@ -114,57 +113,65 @@ impl GlobalState {
self.update_status_or_notify();
if self.config.did_save_text_document_dynamic_registration() {
- let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
- include_text: Some(false),
- text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
- document_selector: Some(vec![
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/*.rs".into()),
- },
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/Cargo.toml".into()),
- },
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/Cargo.lock".into()),
- },
- ]),
- },
- };
-
- let registration = lsp_types::Registration {
- id: "textDocument/didSave".to_string(),
- method: "textDocument/didSave".to_string(),
- register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
- };
- self.send_request::<lsp_types::request::RegisterCapability>(
- lsp_types::RegistrationParams { registrations: vec![registration] },
- |_, _| (),
- );
+ self.register_did_save_capability();
}
- self.fetch_workspaces_queue.request_op("startup".to_string());
- if let Some(cause) = self.fetch_workspaces_queue.should_start_op() {
- self.fetch_workspaces(cause);
+ self.fetch_workspaces_queue.request_op("startup".to_string(), false);
+ if let Some((cause, force_crate_graph_reload)) =
+ self.fetch_workspaces_queue.should_start_op()
+ {
+ self.fetch_workspaces(cause, force_crate_graph_reload);
}
while let Some(event) = self.next_event(&inbox) {
- if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
- if not.method == lsp_types::notification::Exit::METHOD {
- return Ok(());
- }
+ if matches!(
+ &event,
+ Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))
+ if method == lsp_types::notification::Exit::METHOD
+ ) {
+ return Ok(());
}
- self.handle_event(event)?
+ self.handle_event(event)?;
}
Err("client exited without proper shutdown sequence".into())
}
+ fn register_did_save_capability(&mut self) {
+ let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
+ include_text: Some(false),
+ text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
+ document_selector: Some(vec![
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/*.rs".into()),
+ },
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/Cargo.toml".into()),
+ },
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/Cargo.lock".into()),
+ },
+ ]),
+ },
+ };
+
+ let registration = lsp_types::Registration {
+ id: "textDocument/didSave".to_string(),
+ method: "textDocument/didSave".to_string(),
+ register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
+ };
+ self.send_request::<lsp_types::request::RegisterCapability>(
+ lsp_types::RegistrationParams { registrations: vec![registration] },
+ |_, _| (),
+ );
+ }
+
fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
select! {
recv(inbox) -> msg =>
@@ -173,6 +180,9 @@ impl GlobalState {
recv(self.task_pool.receiver) -> task =>
Some(Event::Task(task.unwrap())),
+ recv(self.fmt_pool.receiver) -> task =>
+ Some(Event::Task(task.unwrap())),
+
recv(self.loader.receiver) -> task =>
Some(Event::Vfs(task.unwrap())),
@@ -186,19 +196,20 @@ impl GlobalState {
// NOTE: don't count blocking select! call as a loop-turn time
let _p = profile::span("GlobalState::handle_event");
- tracing::debug!("{:?} handle_event({:?})", loop_start, event);
- let task_queue_len = self.task_pool.handle.len();
- if task_queue_len > 0 {
- tracing::info!("task queue len: {}", task_queue_len);
+ let event_dbg_msg = format!("{event:?}");
+ tracing::debug!("{:?} handle_event({})", loop_start, event_dbg_msg);
+ if tracing::enabled!(tracing::Level::INFO) {
+ let task_queue_len = self.task_pool.handle.len();
+ if task_queue_len > 0 {
+ tracing::info!("task queue len: {}", task_queue_len);
+ }
}
let was_quiescent = self.is_quiescent();
match event {
Event::Lsp(msg) => match msg {
lsp_server::Message::Request(req) => self.on_new_request(loop_start, req),
- lsp_server::Message::Notification(not) => {
- self.on_notification(not)?;
- }
+ lsp_server::Message::Notification(not) => self.on_notification(not)?,
lsp_server::Message::Response(resp) => self.complete_request(resp),
},
Event::Task(task) => {
@@ -247,7 +258,7 @@ impl GlobalState {
self.prime_caches_queue.op_completed(());
if cancelled {
self.prime_caches_queue
- .request_op("restart after cancellation".to_string());
+ .request_op("restart after cancellation".to_string(), ());
}
}
};
@@ -272,6 +283,7 @@ impl GlobalState {
}
}
}
+ let event_handling_duration = loop_start.elapsed();
let state_changed = self.process_changes();
let memdocs_added_or_removed = self.mem_docs.take_changes();
@@ -279,7 +291,8 @@ impl GlobalState {
if self.is_quiescent() {
let became_quiescent = !(was_quiescent
|| self.fetch_workspaces_queue.op_requested()
- || self.fetch_build_data_queue.op_requested());
+ || self.fetch_build_data_queue.op_requested()
+ || self.fetch_proc_macros_queue.op_requested());
if became_quiescent {
if self.config.check_on_save() {
@@ -287,11 +300,12 @@ impl GlobalState {
self.flycheck.iter().for_each(FlycheckHandle::restart);
}
if self.config.prefill_caches() {
- self.prime_caches_queue.request_op("became quiescent".to_string());
+ self.prime_caches_queue.request_op("became quiescent".to_string(), ());
}
}
- if !was_quiescent || state_changed {
+ let client_refresh = !was_quiescent || state_changed;
+ if client_refresh {
// Refresh semantic tokens if the client supports it.
if self.config.semantic_tokens_refresh() {
self.semantic_tokens_cache.lock().clear();
@@ -309,9 +323,9 @@ impl GlobalState {
}
}
- if (!was_quiescent || state_changed || memdocs_added_or_removed)
- && self.config.publish_diagnostics()
- {
+ let update_diagnostics = (!was_quiescent || state_changed || memdocs_added_or_removed)
+ && self.config.publish_diagnostics();
+ if update_diagnostics {
self.update_diagnostics()
}
}
@@ -357,48 +371,56 @@ impl GlobalState {
}
if self.config.cargo_autoreload() {
- if let Some(cause) = self.fetch_workspaces_queue.should_start_op() {
- self.fetch_workspaces(cause);
+ if let Some((cause, force_crate_graph_reload)) =
+ self.fetch_workspaces_queue.should_start_op()
+ {
+ self.fetch_workspaces(cause, force_crate_graph_reload);
}
}
if !self.fetch_workspaces_queue.op_in_progress() {
- if let Some(cause) = self.fetch_build_data_queue.should_start_op() {
+ if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() {
self.fetch_build_data(cause);
+ } else if let Some((cause, paths)) = self.fetch_proc_macros_queue.should_start_op() {
+ self.fetch_proc_macros(cause, paths);
}
}
- if let Some(cause) = self.prime_caches_queue.should_start_op() {
- tracing::debug!(%cause, "will prime caches");
- let num_worker_threads = self.config.prime_caches_num_threads();
-
- self.task_pool.handle.spawn_with_sender({
- let analysis = self.snapshot().analysis;
- move |sender| {
- sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
- let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
- let report = PrimeCachesProgress::Report(progress);
- sender.send(Task::PrimeCaches(report)).unwrap();
- });
- sender
- .send(Task::PrimeCaches(PrimeCachesProgress::End {
- cancelled: res.is_err(),
- }))
- .unwrap();
- }
- });
+ if let Some((cause, ())) = self.prime_caches_queue.should_start_op() {
+ self.prime_caches(cause);
}
self.update_status_or_notify();
let loop_duration = loop_start.elapsed();
if loop_duration > Duration::from_millis(100) && was_quiescent {
- tracing::warn!("overly long loop turn: {:?}", loop_duration);
- self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
+ tracing::warn!("overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}");
+ self.poke_rust_analyzer_developer(format!(
+ "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}"
+ ));
}
Ok(())
}
+ fn prime_caches(&mut self, cause: String) {
+ tracing::debug!(%cause, "will prime caches");
+ let num_worker_threads = self.config.prime_caches_num_threads();
+
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
+ let analysis = self.snapshot().analysis;
+ move |sender| {
+ sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
+ let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
+ let report = PrimeCachesProgress::Report(progress);
+ sender.send(Task::PrimeCaches(report)).unwrap();
+ });
+ sender
+ .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() }))
+ .unwrap();
+ }
+ });
+ }
+
fn update_status_or_notify(&mut self) {
let status = self.current_status();
if self.last_reported_status.as_ref() != Some(&status) {
@@ -406,7 +428,11 @@ impl GlobalState {
if self.config.server_status_notification() {
self.send_notification::<lsp_ext::ServerStatusNotification>(status);
- } else if let (health, Some(message)) = (status.health, &status.message) {
+ } else if let (
+ health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error),
+ Some(message),
+ ) = (status.health, &status.message)
+ {
let open_log_button = tracing::enabled!(tracing::Level::ERROR)
&& (self.fetch_build_data_error().is_err()
|| self.fetch_workspace_error().is_err());
@@ -451,8 +477,9 @@ impl GlobalState {
let (state, msg) = match progress {
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
- ProjectWorkspaceProgress::End(workspaces) => {
- self.fetch_workspaces_queue.op_completed(Some(workspaces));
+ ProjectWorkspaceProgress::End(workspaces, force_reload_crate_graph) => {
+ self.fetch_workspaces_queue
+ .op_completed(Some((workspaces, force_reload_crate_graph)));
if let Err(e) = self.fetch_workspace_error() {
tracing::error!("FetchWorkspaceError:\n{e}");
}
@@ -462,7 +489,8 @@ impl GlobalState {
let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
if self.config.run_build_scripts() && workspaces_updated {
- self.fetch_build_data_queue.request_op(format!("workspace updated"));
+ self.fetch_build_data_queue
+ .request_op(format!("workspace updated"), ());
}
(Progress::End, None)
@@ -488,6 +516,22 @@ impl GlobalState {
};
if let Some(state) = state {
+ self.report_progress("Building", state, msg, None, None);
+ }
+ }
+ Task::LoadProcMacros(progress) => {
+ let (state, msg) = match progress {
+ ProcMacroProgress::Begin => (Some(Progress::Begin), None),
+ ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
+ ProcMacroProgress::End(proc_macro_load_result) => {
+ self.fetch_proc_macros_queue.op_completed(true);
+ self.set_proc_macros(proc_macro_load_result);
+
+ (Some(Progress::End), None)
+ }
+ };
+
+ if let Some(state) = state {
self.report_progress("Loading", state, msg, None, None);
}
}
@@ -512,7 +556,6 @@ impl GlobalState {
self.vfs_progress_n_total = n_total;
self.vfs_progress_n_done = n_done;
- // if n_total != 0 {
let state = if n_done == 0 {
Progress::Begin
} else if n_done < n_total {
@@ -528,7 +571,6 @@ impl GlobalState {
Some(Progress::fraction(n_done, n_total)),
None,
);
- // }
}
}
}
@@ -568,21 +610,18 @@ impl GlobalState {
(Progress::Begin, None)
}
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
- flycheck::Progress::DidCancel => (Progress::End, None),
+ flycheck::Progress::DidCancel => {
+ self.last_flycheck_error = None;
+ (Progress::End, None)
+ }
flycheck::Progress::DidFailToRestart(err) => {
- self.show_and_log_error(
- "cargo check failed to start".to_string(),
- Some(err),
- );
+ self.last_flycheck_error =
+ Some(format!("cargo check failed to start: {err}"));
return;
}
flycheck::Progress::DidFinish(result) => {
- if let Err(err) = result {
- self.show_and_log_error(
- "cargo check failed".to_string(),
- Some(err.to_string()),
- );
- }
+ self.last_flycheck_error =
+ result.err().map(|err| format!("cargo check failed to start: {err}"));
(Progress::End, None)
}
};
@@ -631,18 +670,52 @@ impl GlobalState {
_ => (),
}
+ use crate::handlers::request as handlers;
+
dispatcher
+ // Request handlers that must run on the main thread
+ // because they mutate GlobalState:
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
+ .on_sync_mut::<lsp_ext::RebuildProcMacros>(handlers::handle_proc_macros_rebuild)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
+ // Request handlers which are related to the user typing
+ // are run on the main thread to reduce latency:
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
+ .on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
+ // Formatting should be done immediately as the editor might wait on it, but we can't
+ // put it on the main thread as we do not want the main thread to block on rustfmt.
+ // So we have an extra thread just for formatting requests to make sure it gets handled
+ // as fast as possible.
+ .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
+ .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
+ // We can’t run latency-sensitive request handlers which do semantic
+ // analysis on the main thread because that would block other
+ // requests. Instead, we run these request handlers on higher priority
+ // threads in the threadpool.
+ .on_latency_sensitive::<lsp_types::request::Completion>(handlers::handle_completion)
+ .on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>(
+ handlers::handle_completion_resolve,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>(
+ handlers::handle_semantic_tokens_full,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>(
+ handlers::handle_semantic_tokens_full_delta,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
+ handlers::handle_semantic_tokens_range,
+ )
+ // All other request handlers
+ .on::<lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list)
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
.on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
+ .on::<lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
.on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
@@ -657,7 +730,6 @@ impl GlobalState {
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
.on::<lsp_ext::MoveItem>(handlers::handle_move_item)
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
- .on::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
.on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
@@ -665,8 +737,6 @@ impl GlobalState {
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
.on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints)
.on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
- .on::<lsp_types::request::Completion>(handlers::handle_completion)
- .on::<lsp_types::request::ResolveCompletionItem>(handlers::handle_completion_resolve)
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)
@@ -674,8 +744,6 @@ impl GlobalState {
.on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)
.on::<lsp_types::request::Rename>(handlers::handle_rename)
.on::<lsp_types::request::References>(handlers::handle_references)
- .on::<lsp_types::request::Formatting>(handlers::handle_formatting)
- .on::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
.on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
.on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
.on::<lsp_types::request::CallHierarchyIncomingCalls>(
@@ -684,15 +752,6 @@ impl GlobalState {
.on::<lsp_types::request::CallHierarchyOutgoingCalls>(
handlers::handle_call_hierarchy_outgoing,
)
- .on::<lsp_types::request::SemanticTokensFullRequest>(
- handlers::handle_semantic_tokens_full,
- )
- .on::<lsp_types::request::SemanticTokensFullDeltaRequest>(
- handlers::handle_semantic_tokens_full_delta,
- )
- .on::<lsp_types::request::SemanticTokensRangeRequest>(
- handlers::handle_semantic_tokens_range,
- )
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
.finish();
@@ -700,282 +759,32 @@ impl GlobalState {
/// Handles an incoming notification.
fn on_notification(&mut self, not: Notification) -> Result<()> {
- // FIXME: Move these implementations out into a module similar to on_request
- fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
- let file_id = this.vfs.read().0.file_id(&vfs_path);
- if let Some(file_id) = file_id {
- let world = this.snapshot();
- let mut updated = false;
- let task = move || -> std::result::Result<(), ide::Cancelled> {
- // Trigger flychecks for all workspaces that depend on the saved file
- // Crates containing or depending on the saved file
- let crate_ids: Vec<_> = world
- .analysis
- .crates_for(file_id)?
- .into_iter()
- .flat_map(|id| world.analysis.transitive_rev_deps(id))
- .flatten()
- .sorted()
- .unique()
- .collect();
-
- let crate_root_paths: Vec<_> = crate_ids
- .iter()
- .filter_map(|&crate_id| {
- world
- .analysis
- .crate_root(crate_id)
- .map(|file_id| {
- world
- .file_id_to_file_path(file_id)
- .as_path()
- .map(ToOwned::to_owned)
- })
- .transpose()
- })
- .collect::<ide::Cancellable<_>>()?;
- let crate_root_paths: Vec<_> =
- crate_root_paths.iter().map(Deref::deref).collect();
-
- // Find all workspaces that have at least one target containing the saved file
- let workspace_ids =
- world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
- project_model::ProjectWorkspace::Cargo { cargo, .. } => {
- cargo.packages().any(|pkg| {
- cargo[pkg].targets.iter().any(|&it| {
- crate_root_paths.contains(&cargo[it].root.as_path())
- })
- })
- }
- project_model::ProjectWorkspace::Json { project, .. } => project
- .crates()
- .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
- project_model::ProjectWorkspace::DetachedFiles { .. } => false,
- });
-
- // Find and trigger corresponding flychecks
- for flycheck in world.flycheck.iter() {
- for (id, _) in workspace_ids.clone() {
- if id == flycheck.id() {
- updated = true;
- flycheck.restart();
- continue;
- }
- }
- }
- // No specific flycheck was triggered, so let's trigger all of them.
- if !updated {
- for flycheck in world.flycheck.iter() {
- flycheck.restart();
- }
- }
- Ok(())
- };
- this.task_pool.handle.spawn_with_sender(move |_| {
- if let Err(e) = std::panic::catch_unwind(task) {
- tracing::error!("flycheck task panicked: {e:?}")
- }
- });
- true
- } else {
- false
- }
- }
+ use crate::handlers::notification as handlers;
+ use lsp_types::notification as notifs;
NotificationDispatcher { not: Some(not), global_state: self }
- .on::<lsp_types::notification::Cancel>(|this, params| {
- let id: lsp_server::RequestId = match params.id {
- lsp_types::NumberOrString::Number(id) => id.into(),
- lsp_types::NumberOrString::String(id) => id.into(),
- };
- this.cancel(id);
- Ok(())
- })?
- .on::<lsp_types::notification::WorkDoneProgressCancel>(|this, params| {
- if let lsp_types::NumberOrString::String(s) = &params.token {
- if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") {
- if let Ok(id) = u32::from_str_radix(id, 10) {
- if let Some(flycheck) = this.flycheck.get(id as usize) {
- flycheck.cancel();
- }
- }
- }
- }
- // Just ignore this. It is OK to continue sending progress
- // notifications for this token, as the client can't know when
- // we accepted notification.
- Ok(())
- })?
- .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- let already_exists = this
- .mem_docs
- .insert(path.clone(), DocumentData::new(params.text_document.version))
- .is_err();
- if already_exists {
- tracing::error!("duplicate DidOpenTextDocument: {}", path);
- }
- this.vfs
- .write()
- .0
- .set_file_contents(path, Some(params.text_document.text.into_bytes()));
- }
- Ok(())
- })?
- .on::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
- .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- match this.mem_docs.get_mut(&path) {
- Some(doc) => {
- // The version passed in DidChangeTextDocument is the version after all edits are applied
- // so we should apply it before the vfs is notified.
- doc.version = params.text_document.version;
- }
- None => {
- tracing::error!("unexpected DidChangeTextDocument: {}", path);
- return Ok(());
- }
- };
-
- let vfs = &mut this.vfs.write().0;
- let file_id = vfs.file_id(&path).unwrap();
- let text = apply_document_changes(
- this.config.position_encoding(),
- || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
- params.content_changes,
- );
-
- vfs.set_file_contents(path, Some(text.into_bytes()));
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- if this.mem_docs.remove(&path).is_err() {
- tracing::error!("orphan DidCloseTextDocument: {}", path);
- }
-
- this.semantic_tokens_cache.lock().remove(&params.text_document.uri);
-
- if let Some(path) = path.as_path() {
- this.loader.handle.invalidate(path.to_path_buf());
- }
- }
- Ok(())
- })?
- .on::<lsp_ext::ClearFlycheck>(|this, ()| {
- this.diagnostics.clear_check_all();
- Ok(())
- })?
- .on::<lsp_ext::RunFlycheck>(|this, params| {
- if let Some(text_document) = params.text_document {
- if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
- if run_flycheck(this, vfs_path) {
- return Ok(());
- }
- }
- }
- // No specific flycheck was triggered, so let's trigger all of them.
- for flycheck in this.flycheck.iter() {
- flycheck.restart();
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
- if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
- // Re-fetch workspaces if a workspace related file has changed
- if let Some(abs_path) = vfs_path.as_path() {
- if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
- this.fetch_workspaces_queue
- .request_op(format!("DidSaveTextDocument {}", abs_path.display()));
- }
- }
-
- if !this.config.check_on_save() || run_flycheck(this, vfs_path) {
- return Ok(());
- }
- } else if this.config.check_on_save() {
- // No specific flycheck was triggered, so let's trigger all of them.
- for flycheck in this.flycheck.iter() {
- flycheck.restart();
- }
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
- // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
- // this notification's parameters should be ignored and the actual config queried separately.
- this.send_request::<lsp_types::request::WorkspaceConfiguration>(
- lsp_types::ConfigurationParams {
- items: vec![lsp_types::ConfigurationItem {
- scope_uri: None,
- section: Some("rust-analyzer".to_string()),
- }],
- },
- |this, resp| {
- tracing::debug!("config update response: '{:?}", resp);
- let lsp_server::Response { error, result, .. } = resp;
-
- match (error, result) {
- (Some(err), _) => {
- tracing::error!("failed to fetch the server settings: {:?}", err)
- }
- (None, Some(mut configs)) => {
- if let Some(json) = configs.get_mut(0) {
- // Note that json can be null according to the spec if the client can't
- // provide a configuration. This is handled in Config::update below.
- let mut config = Config::clone(&*this.config);
- if let Err(error) = config.update(json.take()) {
- this.show_message(
- lsp_types::MessageType::WARNING,
- error.to_string(),
- false,
- );
- }
- this.update_configuration(config);
- }
- }
- (None, None) => tracing::error!(
- "received empty server settings response from the client"
- ),
- }
- },
- );
-
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeWorkspaceFolders>(|this, params| {
- let config = Arc::make_mut(&mut this.config);
-
- for workspace in params.event.removed {
- let Ok(path) = workspace.uri.to_file_path() else { continue };
- let Ok(path) = AbsPathBuf::try_from(path) else { continue };
- let Some(position) = config.workspace_roots.iter().position(|it| it == &path) else { continue };
- config.workspace_roots.remove(position);
- }
-
- let added = params
- .event
- .added
- .into_iter()
- .filter_map(|it| it.uri.to_file_path().ok())
- .filter_map(|it| AbsPathBuf::try_from(it).ok());
- config.workspace_roots.extend(added);
- if !config.has_linked_projects() && config.detached_files().is_empty() {
- config.rediscover_workspaces();
- this.fetch_workspaces_queue.request_op("client workspaces changed".to_string())
- }
-
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
- for change in params.changes {
- if let Ok(path) = from_proto::abs_path(&change.uri) {
- this.loader.handle.invalidate(path);
- }
- }
- Ok(())
- })?
+ .on_sync_mut::<notifs::Cancel>(handlers::handle_cancel)?
+ .on_sync_mut::<notifs::WorkDoneProgressCancel>(
+ handlers::handle_work_done_progress_cancel,
+ )?
+ .on_sync_mut::<notifs::DidOpenTextDocument>(handlers::handle_did_open_text_document)?
+ .on_sync_mut::<notifs::DidChangeTextDocument>(
+ handlers::handle_did_change_text_document,
+ )?
+ .on_sync_mut::<notifs::DidCloseTextDocument>(handlers::handle_did_close_text_document)?
+ .on_sync_mut::<notifs::DidSaveTextDocument>(handlers::handle_did_save_text_document)?
+ .on_sync_mut::<notifs::DidChangeConfiguration>(
+ handlers::handle_did_change_configuration,
+ )?
+ .on_sync_mut::<notifs::DidChangeWorkspaceFolders>(
+ handlers::handle_did_change_workspace_folders,
+ )?
+ .on_sync_mut::<notifs::DidChangeWatchedFiles>(
+ handlers::handle_did_change_watched_files,
+ )?
+ .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
+ .on_sync_mut::<lsp_ext::ClearFlycheck>(handlers::handle_clear_flycheck)?
+ .on_sync_mut::<lsp_ext::RunFlycheck>(handlers::handle_run_flycheck)?
.finish();
Ok(())
}
@@ -1000,16 +809,60 @@ impl GlobalState {
tracing::trace!("updating notifications for {:?}", subscriptions);
let snapshot = self.snapshot();
- self.task_pool.handle.spawn(move || {
+
+ // Diagnostics are triggered by the user typing
+ // so we run them on a latency sensitive thread.
+ self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
+ let _p = profile::span("publish_diagnostics");
+ let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
let diagnostics = subscriptions
.into_iter()
.filter_map(|file_id| {
- handlers::publish_diagnostics(&snapshot, file_id)
- .ok()
- .map(|diags| (file_id, diags))
+ let line_index = snapshot.file_line_index(file_id).ok()?;
+ Some((
+ file_id,
+ line_index,
+ snapshot
+ .analysis
+ .diagnostics(
+ &snapshot.config.diagnostics(),
+ ide::AssistResolveStrategy::None,
+ file_id,
+ )
+ .ok()?,
+ ))
})
- .collect::<Vec<_>>();
- Task::Diagnostics(diagnostics)
- })
+ .map(|(file_id, line_index, it)| {
+ (
+ file_id,
+ it.into_iter()
+ .map(move |d| lsp_types::Diagnostic {
+ range: crate::to_proto::range(&line_index, d.range),
+ severity: Some(crate::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(
+ d.code.as_str().to_string(),
+ )),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&format!(
+ "https://rust-analyzer.github.io/manual.html#{}",
+ d.code.as_str()
+ ))
+ .unwrap(),
+ }),
+ source: Some("rust-analyzer".to_string()),
+ message: d.message,
+ related_information: None,
+ tags: if d.unused {
+ Some(vec![lsp_types::DiagnosticTag::UNNECESSARY])
+ } else {
+ None
+ },
+ data: None,
+ })
+ .collect::<Vec<_>>(),
+ )
+ });
+ Task::Diagnostics(diagnostics.collect())
+ });
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
index 912ed1e76..58426c66a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
@@ -28,7 +28,7 @@ pub(crate) fn format_docs(src: &str) -> String {
if in_code_block {
let trimmed = line.trim_start();
- if trimmed.starts_with("##") {
+ if is_rust && trimmed.starts_with("##") {
line = &trimmed[1..];
}
}
@@ -154,4 +154,12 @@ let s = "foo
assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
}
+
+ #[test]
+ fn test_format_docs_handles_double_hashes_non_rust() {
+ let comment = r#"```markdown
+## A second-level heading
+```"#;
+ assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
+ }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 97aca0161..932730fc2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -3,23 +3,23 @@
pub(crate) type Cause = String;
-pub(crate) struct OpQueue<Output> {
- op_requested: Option<Cause>,
+pub(crate) struct OpQueue<Args = (), Output = ()> {
+ op_requested: Option<(Cause, Args)>,
op_in_progress: bool,
last_op_result: Output,
}
-impl<Output: Default> Default for OpQueue<Output> {
+impl<Args, Output: Default> Default for OpQueue<Args, Output> {
fn default() -> Self {
Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
}
}
-impl<Output> OpQueue<Output> {
- pub(crate) fn request_op(&mut self, reason: Cause) {
- self.op_requested = Some(reason);
+impl<Args, Output> OpQueue<Args, Output> {
+ pub(crate) fn request_op(&mut self, reason: Cause, args: Args) {
+ self.op_requested = Some((reason, args));
}
- pub(crate) fn should_start_op(&mut self) -> Option<Cause> {
+ pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> {
if self.op_in_progress {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 1a6e1af2e..310c6b076 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -12,22 +12,25 @@
//! correct. Instead, we try to provide a best-effort service. Even if the
//! project is currently loading and we don't have a full project model, we
//! still want to respond to various requests.
-use std::{collections::hash_map::Entry, mem, sync::Arc};
+use std::{collections::hash_map::Entry, iter, mem, sync};
use flycheck::{FlycheckConfig, FlycheckHandle};
use hir::db::DefDatabase;
use ide::Change;
use ide_db::{
base_db::{
- CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
- ProcMacroLoadResult, SourceRoot, VfsPath,
+ salsa::Durability, CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
+ ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, VfsPath,
},
FxHashMap,
};
use itertools::Itertools;
use proc_macro_api::{MacroDylib, ProcMacroServer};
use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
+use rustc_hash::FxHashSet;
+use stdx::{format_to, thread::ThreadIntent};
use syntax::SmolStr;
+use triomphe::Arc;
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
use crate::{
@@ -44,7 +47,7 @@ use ::tt::token_id as tt;
pub(crate) enum ProjectWorkspaceProgress {
Begin,
Report(String),
- End(Vec<anyhow::Result<ProjectWorkspace>>),
+ End(Vec<anyhow::Result<ProjectWorkspace>>, bool),
}
#[derive(Debug)]
@@ -54,11 +57,19 @@ pub(crate) enum BuildDataProgress {
End((Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)),
}
+#[derive(Debug)]
+pub(crate) enum ProcMacroProgress {
+ Begin,
+ Report(String),
+ End(ProcMacros),
+}
+
impl GlobalState {
pub(crate) fn is_quiescent(&self) -> bool {
!(self.last_reported_status.is_none()
|| self.fetch_workspaces_queue.op_in_progress()
|| self.fetch_build_data_queue.op_in_progress()
+ || self.fetch_proc_macros_queue.op_in_progress()
|| self.vfs_progress_config_version < self.vfs_config_version
|| self.vfs_progress_n_done < self.vfs_progress_n_total)
}
@@ -66,21 +77,27 @@ impl GlobalState {
pub(crate) fn update_configuration(&mut self, config: Config) {
let _p = profile::span("GlobalState::update_configuration");
let old_config = mem::replace(&mut self.config, Arc::new(config));
- if self.config.lru_capacity() != old_config.lru_capacity() {
- self.analysis_host.update_lru_capacity(self.config.lru_capacity());
+ if self.config.lru_parse_query_capacity() != old_config.lru_parse_query_capacity() {
+ self.analysis_host.update_lru_capacity(self.config.lru_parse_query_capacity());
+ }
+ if self.config.lru_query_capacities() != old_config.lru_query_capacities() {
+ self.analysis_host.update_lru_capacities(
+ &self.config.lru_query_capacities().cloned().unwrap_or_default(),
+ );
}
if self.config.linked_projects() != old_config.linked_projects() {
- self.fetch_workspaces_queue.request_op("linked projects changed".to_string())
+ self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), false)
} else if self.config.flycheck() != old_config.flycheck() {
self.reload_flycheck();
}
- if self.analysis_host.raw_database().enable_proc_attr_macros()
+ if self.analysis_host.raw_database().expand_proc_attr_macros()
!= self.config.expand_proc_attr_macros()
{
- self.analysis_host
- .raw_database_mut()
- .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
+ self.analysis_host.raw_database_mut().set_expand_proc_attr_macros_with_durability(
+ self.config.expand_proc_attr_macros(),
+ Durability::HIGH,
+ );
}
}
@@ -94,12 +111,16 @@ impl GlobalState {
if self.proc_macro_changed {
status.health = lsp_ext::Health::Warning;
- message.push_str("Reload required due to source changes of a procedural macro.\n\n");
+ message.push_str("Proc-macros have changed and need to be rebuilt.\n\n");
}
if let Err(_) = self.fetch_build_data_error() {
status.health = lsp_ext::Health::Warning;
message.push_str("Failed to run build scripts of some packages.\n\n");
}
+ if self.proc_macro_clients.iter().any(|it| it.is_err()) {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str("Failed to spawn one or more proc-macro servers.\n\n");
+ }
if !self.config.cargo_autoreload()
&& self.is_quiescent()
&& self.fetch_workspaces_queue.op_requested()
@@ -112,17 +133,37 @@ impl GlobalState {
&& self.config.notifications().cargo_toml_not_found
{
status.health = lsp_ext::Health::Warning;
- message.push_str("Failed to discover workspace.\n\n");
+ message.push_str("Failed to discover workspace.\n");
+ message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n");
+ }
+ if let Some(err) = &self.config_errors {
+ status.health = lsp_ext::Health::Warning;
+ format_to!(message, "{err}\n");
+ }
+ if let Some(err) = &self.last_flycheck_error {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(err);
+ message.push('\n');
}
for ws in self.workspaces.iter() {
let (ProjectWorkspace::Cargo { sysroot, .. }
| ProjectWorkspace::Json { sysroot, .. }
| ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws;
- if let Err(Some(e)) = sysroot {
- status.health = lsp_ext::Health::Warning;
- message.push_str(e);
- message.push_str("\n\n");
+ match sysroot {
+ Err(None) => (),
+ Err(Some(e)) => {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(e);
+ message.push_str("\n\n");
+ }
+ Ok(s) => {
+ if let Some(e) = s.loading_warning() {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(&e);
+ message.push_str("\n\n");
+ }
+ }
}
if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws {
status.health = lsp_ext::Health::Warning;
@@ -142,10 +183,10 @@ impl GlobalState {
status
}
- pub(crate) fn fetch_workspaces(&mut self, cause: Cause) {
+ pub(crate) fn fetch_workspaces(&mut self, cause: Cause, force_crate_graph_reload: bool) {
tracing::info!(%cause, "will fetch workspaces");
- self.task_pool.handle.spawn_with_sender({
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
let linked_projects = self.config.linked_projects();
let detached_files = self.config.detached_files().to_vec();
let cargo_config = self.config.cargo();
@@ -177,11 +218,30 @@ impl GlobalState {
it.clone(),
cargo_config.target.as_deref(),
&cargo_config.extra_env,
+ None,
))
}
})
.collect::<Vec<_>>();
+ let mut i = 0;
+ while i < workspaces.len() {
+ if let Ok(w) = &workspaces[i] {
+ let dupes: Vec<_> = workspaces
+ .iter()
+ .enumerate()
+ .skip(i + 1)
+ .filter_map(|(i, it)| {
+ it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i)
+ })
+ .collect();
+ dupes.into_iter().rev().for_each(|d| {
+ _ = workspaces.remove(d);
+ });
+ }
+ i += 1;
+ }
+
if !detached_files.is_empty() {
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
detached_files,
@@ -191,7 +251,10 @@ impl GlobalState {
tracing::info!("did fetch workspaces {:?}", workspaces);
sender
- .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
+ .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(
+ workspaces,
+ force_crate_graph_reload,
+ )))
.unwrap();
}
});
@@ -201,7 +264,7 @@ impl GlobalState {
tracing::info!(%cause, "will fetch build data");
let workspaces = Arc::clone(&self.workspaces);
let config = self.config.cargo();
- self.task_pool.handle.spawn_with_sender(move |sender| {
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
let progress = {
@@ -216,19 +279,80 @@ impl GlobalState {
});
}
+ pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
+ tracing::info!(%cause, "will load proc macros");
+ let dummy_replacements = self.config.dummy_replacements().clone();
+ let proc_macro_clients = self.proc_macro_clients.clone();
+
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
+
+ let dummy_replacements = &dummy_replacements;
+ let progress = {
+ let sender = sender.clone();
+ &move |msg| {
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap()
+ }
+ };
+
+ let mut res = FxHashMap::default();
+ let chain = proc_macro_clients
+ .iter()
+ .map(|res| res.as_ref().map_err(|e| e.to_string()))
+ .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into())));
+ for (client, paths) in chain.zip(paths) {
+ res.extend(paths.into_iter().map(move |(crate_id, res)| {
+ (
+ crate_id,
+ res.map_or_else(
+ |_| Err("proc macro crate is missing dylib".to_owned()),
+ |(crate_name, path)| {
+ progress(path.display().to_string());
+ client.as_ref().map_err(Clone::clone).and_then(|client| {
+ load_proc_macro(
+ client,
+ &path,
+ crate_name
+ .as_deref()
+ .and_then(|crate_name| {
+ dummy_replacements.get(crate_name).map(|v| &**v)
+ })
+ .unwrap_or_default(),
+ )
+ })
+ },
+ ),
+ )
+ }));
+ }
+
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap();
+ });
+ }
+
+ pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
+ let mut change = Change::new();
+ change.set_proc_macros(proc_macros);
+ self.analysis_host.apply_change(change);
+ }
+
pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
let _p = profile::span("GlobalState::switch_workspaces");
tracing::info!(%cause, "will switch workspaces");
+ let Some((workspaces, force_reload_crate_graph)) = self.fetch_workspaces_queue.last_op_result() else { return; };
+
if let Err(_) = self.fetch_workspace_error() {
if !self.workspaces.is_empty() {
+ if *force_reload_crate_graph {
+ self.recreate_crate_graph(cause);
+ }
// It only makes sense to switch to a partially broken workspace
// if we don't have any workspace at all yet.
return;
}
}
- let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
let workspaces =
workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
@@ -257,6 +381,9 @@ impl GlobalState {
self.workspaces = Arc::new(workspaces);
} else {
tracing::info!("build scripts do not match the version of the active workspace");
+ if *force_reload_crate_graph {
+ self.recreate_crate_graph(cause);
+ }
// Current build scripts do not match the version of the active
// workspace, so there's nothing for us to update.
return;
@@ -303,43 +430,39 @@ impl GlobalState {
);
}
- let mut change = Change::new();
-
let files_config = self.config.files();
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
- if self.proc_macro_clients.is_empty() {
- if let Some((path, path_manually_set)) = self.config.proc_macro_srv() {
+ if self.proc_macro_clients.is_empty() || !same_workspaces {
+ if self.config.expand_proc_macros() {
tracing::info!("Spawning proc-macro servers");
- self.proc_macro_clients = self
- .workspaces
- .iter()
- .map(|ws| {
- let (path, args): (_, &[_]) = if path_manually_set {
- tracing::debug!(
- "Pro-macro server path explicitly set: {}",
- path.display()
- );
- (path.clone(), &[])
- } else {
- match ws.find_sysroot_proc_macro_srv() {
- Some(server_path) => (server_path, &[]),
- None => (path.clone(), &["proc-macro"]),
- }
- };
-
- tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
- ProcMacroServer::spawn(path.clone(), args).map_err(|err| {
- let error = format!(
- "Failed to run proc-macro server from path {}, error: {:?}",
- path.display(),
- err
- );
- tracing::error!(error);
- error
+
+ // FIXME: use `Arc::from_iter` when it becomes available
+ self.proc_macro_clients = Arc::from(
+ self.workspaces
+ .iter()
+ .map(|ws| {
+ let path = match self.config.proc_macro_srv() {
+ Some(path) => path,
+ None => ws.find_sysroot_proc_macro_srv()?,
+ };
+
+ tracing::info!("Using proc-macro server at {}", path.display(),);
+ ProcMacroServer::spawn(path.clone()).map_err(|err| {
+ tracing::error!(
+ "Failed to run proc-macro server from path {}, error: {:?}",
+ path.display(),
+ err
+ );
+ anyhow::anyhow!(
+ "Failed to run proc-macro server from path {}, error: {:?}",
+ path.display(),
+ err
+ )
+ })
})
- })
- .collect()
+ .collect::<Vec<_>>(),
+ )
};
}
@@ -353,63 +476,65 @@ impl GlobalState {
watch,
version: self.vfs_config_version,
});
+ self.source_root_config = project_folders.source_root_config;
- // Create crate graph from all the workspaces
- let crate_graph = {
- let dummy_replacements = self.config.dummy_replacements();
+ self.recreate_crate_graph(cause);
+ tracing::info!("did switch workspaces");
+ }
+
+ fn recreate_crate_graph(&mut self, cause: String) {
+ // Create crate graph from all the workspaces
+ let (crate_graph, proc_macro_paths, crate_graph_file_dependencies) = {
let vfs = &mut self.vfs.write().0;
let loader = &mut self.loader;
- let mem_docs = &self.mem_docs;
- let mut load = move |path: &AbsPath| {
+ // crate graph construction relies on these paths, record them so when one of them gets
+ // deleted or created we trigger a reconstruction of the crate graph
+ let mut crate_graph_file_dependencies = FxHashSet::default();
+
+ let mut load = |path: &AbsPath| {
let _p = profile::span("switch_workspaces::load");
let vfs_path = vfs::VfsPath::from(path.to_path_buf());
- if !mem_docs.contains(&vfs_path) {
- let contents = loader.handle.load_sync(path);
- vfs.set_file_contents(vfs_path.clone(), contents);
- }
- let res = vfs.file_id(&vfs_path);
- if res.is_none() {
- tracing::warn!("failed to load {}", path.display())
+ crate_graph_file_dependencies.insert(vfs_path.clone());
+ match vfs.file_id(&vfs_path) {
+ Some(file_id) => Some(file_id),
+ None => {
+ if !self.mem_docs.contains(&vfs_path) {
+ let contents = loader.handle.load_sync(path);
+ vfs.set_file_contents(vfs_path.clone(), contents);
+ }
+ vfs.file_id(&vfs_path)
+ }
}
- res
};
let mut crate_graph = CrateGraph::default();
- for (idx, ws) in self.workspaces.iter().enumerate() {
- let proc_macro_client = match self.proc_macro_clients.get(idx) {
- Some(res) => res.as_ref().map_err(|e| &**e),
- None => Err("Proc macros are disabled"),
- };
- let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| {
- load_proc_macro(
- proc_macro_client,
- path,
- dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
- )
- };
- crate_graph.extend(ws.to_crate_graph(
- &mut load_proc_macro,
- &mut load,
- &self.config.cargo().extra_env,
- ));
+ let mut proc_macros = Vec::default();
+ for ws in &**self.workspaces {
+ let (other, mut crate_proc_macros) =
+ ws.to_crate_graph(&mut load, &self.config.extra_env());
+ crate_graph.extend(other, &mut crate_proc_macros);
+ proc_macros.push(crate_proc_macros);
}
- crate_graph
+ (crate_graph, proc_macros, crate_graph_file_dependencies)
};
- change.set_crate_graph(crate_graph);
-
- self.source_root_config = project_folders.source_root_config;
+ if self.config.expand_proc_macros() {
+ self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
+ }
+ let mut change = Change::new();
+ change.set_crate_graph(crate_graph);
self.analysis_host.apply_change(change);
+ self.crate_graph_file_dependencies = crate_graph_file_dependencies;
self.process_changes();
+
self.reload_flycheck();
- tracing::info!("did switch workspaces");
}
pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
let mut buf = String::new();
- let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
+ let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
if last_op_result.is_empty() {
stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
} else {
@@ -642,14 +767,12 @@ impl SourceRootConfig {
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
/// with an identity dummy expander.
pub(crate) fn load_proc_macro(
- server: Result<&ProcMacroServer, &str>,
+ server: &ProcMacroServer,
path: &AbsPath,
dummy_replace: &[Box<str>],
) -> ProcMacroLoadResult {
- let server = server.map_err(ToOwned::to_owned)?;
let res: Result<Vec<_>, String> = (|| {
- let dylib = MacroDylib::new(path.to_path_buf())
- .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
+ let dylib = MacroDylib::new(path.to_path_buf());
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
if vec.is_empty() {
return Err("proc macro library returned no proc macros".to_string());
@@ -684,14 +807,14 @@ pub(crate) fn load_proc_macro(
proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
};
- let expander: Arc<dyn ProcMacroExpander> =
+ let expander: sync::Arc<dyn ProcMacroExpander> =
if dummy_replace.iter().any(|replace| &**replace == name) {
match kind {
- ProcMacroKind::Attr => Arc::new(IdentityExpander),
- _ => Arc::new(EmptyExpander),
+ ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
+ _ => sync::Arc::new(EmptyExpander),
}
} else {
- Arc::new(Expander(expander))
+ sync::Arc::new(Expander(expander))
};
ProcMacro { name, kind, expander }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
index c2cc3f422..d4bb20c8f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
@@ -13,7 +13,7 @@ macro_rules! define_semantic_token_types {
$($standard:ident),*$(,)?
}
custom {
- $(($custom:ident, $string:literal)),*$(,)?
+ $(($custom:ident, $string:literal) $(=> $fallback:ident)?),*$(,)?
}
) => {
@@ -24,6 +24,15 @@ macro_rules! define_semantic_token_types {
$(SemanticTokenType::$standard,)*
$($custom),*
];
+
+ pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option<SemanticTokenType> {
+ $(
+ if token == $custom {
+ None $(.or(Some(SemanticTokenType::$fallback)))?
+ } else
+ )*
+ { Some(token )}
+ }
};
}
@@ -51,42 +60,46 @@ define_semantic_token_types![
custom {
(ANGLE, "angle"),
- (ARITHMETIC, "arithmetic"),
- (ATTRIBUTE, "attribute"),
- (ATTRIBUTE_BRACKET, "attributeBracket"),
- (BITWISE, "bitwise"),
+ (ARITHMETIC, "arithmetic") => OPERATOR,
+ (ATTRIBUTE, "attribute") => DECORATOR,
+ (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR,
+ (BITWISE, "bitwise") => OPERATOR,
(BOOLEAN, "boolean"),
(BRACE, "brace"),
(BRACKET, "bracket"),
- (BUILTIN_ATTRIBUTE, "builtinAttribute"),
+ (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR,
(BUILTIN_TYPE, "builtinType"),
- (CHAR, "character"),
+ (CHAR, "character") => STRING,
(COLON, "colon"),
(COMMA, "comma"),
- (COMPARISON, "comparison"),
+ (COMPARISON, "comparison") => OPERATOR,
(CONST_PARAMETER, "constParameter"),
- (DERIVE, "derive"),
- (DERIVE_HELPER, "deriveHelper"),
+ (DERIVE, "derive") => DECORATOR,
+ (DERIVE_HELPER, "deriveHelper") => DECORATOR,
(DOT, "dot"),
- (ESCAPE_SEQUENCE, "escapeSequence"),
- (FORMAT_SPECIFIER, "formatSpecifier"),
- (GENERIC, "generic"),
+ (ESCAPE_SEQUENCE, "escapeSequence") => STRING,
+ (FORMAT_SPECIFIER, "formatSpecifier") => STRING,
+ (GENERIC, "generic") => TYPE_PARAMETER,
(LABEL, "label"),
(LIFETIME, "lifetime"),
- (LOGICAL, "logical"),
- (MACRO_BANG, "macroBang"),
+ (LOGICAL, "logical") => OPERATOR,
+ (MACRO_BANG, "macroBang") => MACRO,
(PARENTHESIS, "parenthesis"),
(PUNCTUATION, "punctuation"),
- (SELF_KEYWORD, "selfKeyword"),
- (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
+ (SELF_KEYWORD, "selfKeyword") => KEYWORD,
+ (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD,
(SEMICOLON, "semicolon"),
(TYPE_ALIAS, "typeAlias"),
- (TOOL_MODULE, "toolModule"),
+ (TOOL_MODULE, "toolModule") => DECORATOR,
(UNION, "union"),
(UNRESOLVED_REFERENCE, "unresolvedReference"),
}
];
+macro_rules! count_tts {
+ () => {0usize};
+ ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
+}
macro_rules! define_semantic_token_modifiers {
(
standard {
@@ -105,6 +118,8 @@ macro_rules! define_semantic_token_modifiers {
$(SemanticTokenModifier::$standard,)*
$($custom),*
];
+
+ const LAST_STANDARD_MOD: usize = count_tts!($($standard)*);
};
}
@@ -126,6 +141,7 @@ define_semantic_token_modifiers![
(INJECTED, "injected"),
(INTRA_DOC_LINK, "intraDocLink"),
(LIBRARY, "library"),
+ (MACRO_MODIFIER, "macro"),
(MUTABLE, "mutable"),
(PUBLIC, "public"),
(REFERENCE, "reference"),
@@ -137,6 +153,13 @@ define_semantic_token_modifiers![
#[derive(Default)]
pub(crate) struct ModifierSet(pub(crate) u32);
+impl ModifierSet {
+ pub(crate) fn standard_fallback(&mut self) {
+ // Remove all non standard modifiers
+ self.0 = self.0 & !(!0u32 << LAST_STANDARD_MOD)
+ }
+}
+
impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
index 616e44998..a5a10e869 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
@@ -1,53 +1,42 @@
-//! A thin wrapper around `ThreadPool` to make sure that we join all things
-//! properly.
+//! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs.
+//! It is used in [`crate::global_state::GlobalState`] throughout the main loop.
+
use crossbeam_channel::Sender;
+use stdx::thread::{Pool, ThreadIntent};
pub(crate) struct TaskPool<T> {
sender: Sender<T>,
- inner: threadpool::ThreadPool,
+ pool: Pool,
}
impl<T> TaskPool<T> {
pub(crate) fn new_with_threads(sender: Sender<T>, threads: usize) -> TaskPool<T> {
- const STACK_SIZE: usize = 8 * 1024 * 1024;
-
- let inner = threadpool::Builder::new()
- .thread_name("Worker".into())
- .thread_stack_size(STACK_SIZE)
- .num_threads(threads)
- .build();
- TaskPool { sender, inner }
+ TaskPool { sender, pool: Pool::new(threads) }
}
- pub(crate) fn spawn<F>(&mut self, task: F)
+ pub(crate) fn spawn<F>(&mut self, intent: ThreadIntent, task: F)
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
- self.inner.execute({
+ self.pool.spawn(intent, {
let sender = self.sender.clone();
move || sender.send(task()).unwrap()
})
}
- pub(crate) fn spawn_with_sender<F>(&mut self, task: F)
+ pub(crate) fn spawn_with_sender<F>(&mut self, intent: ThreadIntent, task: F)
where
F: FnOnce(Sender<T>) + Send + 'static,
T: Send + 'static,
{
- self.inner.execute({
+ self.pool.spawn(intent, {
let sender = self.sender.clone();
move || task(sender)
})
}
pub(crate) fn len(&self) -> usize {
- self.inner.queued_count()
- }
-}
-
-impl<T> Drop for TaskPool<T> {
- fn drop(&mut self) {
- self.inner.join()
+ self.pool.len()
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 7d97b69f8..648bc995a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -24,7 +24,7 @@ use crate::{
line_index::{LineEndings, LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
- semantic_tokens,
+ semantic_tokens::{self, standard_fallback_type},
};
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
@@ -32,7 +32,7 @@ pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::P
match line_index.encoding {
PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
PositionEncoding::Wide(enc) => {
- let line_col = line_index.index.to_wide(enc, line_col);
+ let line_col = line_index.index.to_wide(enc, line_col).unwrap();
lsp_types::Position::new(line_col.line, line_col.col)
}
}
@@ -279,7 +279,7 @@ fn completion_item(
let mut lsp_item = lsp_types::CompletionItem {
label: item.label.to_string(),
- detail: item.detail.map(|it| it.to_string()),
+ detail: item.detail,
filter_text: Some(lookup),
kind: Some(completion_item_kind(item.kind)),
text_edit: Some(text_edit),
@@ -306,12 +306,10 @@ fn completion_item(
let imports: Vec<_> = item
.import_to_add
.into_iter()
- .filter_map(|import_edit| {
- let import_path = &import_edit.import_path;
- let import_name = import_path.segments().last()?;
+ .filter_map(|(import_path, import_name)| {
Some(lsp_ext::CompletionImport {
- full_import_path: import_path.to_string(),
- imported_name: import_name.to_string(),
+ full_import_path: import_path,
+ imported_name: import_name,
})
})
.collect();
@@ -412,7 +410,7 @@ pub(crate) fn signature_help(
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
- value: doc,
+ value: crate::markdown::format_docs(&doc),
})
});
@@ -434,83 +432,23 @@ pub(crate) fn signature_help(
pub(crate) fn inlay_hint(
snap: &GlobalStateSnapshot,
line_index: &LineIndex,
- render_colons: bool,
- mut inlay_hint: InlayHint,
+ inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- match inlay_hint.kind {
- InlayKind::Parameter if render_colons => inlay_hint.label.append_str(":"),
- InlayKind::Type if render_colons => inlay_hint.label.prepend_str(": "),
- InlayKind::ClosureReturnType => inlay_hint.label.prepend_str(" -> "),
- InlayKind::Discriminant => inlay_hint.label.prepend_str(" = "),
- _ => {}
- }
-
let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
Ok(lsp_types::InlayHint {
- position: match inlay_hint.kind {
- // before annotated thing
- InlayKind::OpeningParenthesis
- | InlayKind::Parameter
- | InlayKind::Adjustment
- | InlayKind::BindingMode => position(line_index, inlay_hint.range.start()),
- // after annotated thing
- InlayKind::ClosureReturnType
- | InlayKind::Type
- | InlayKind::Discriminant
- | InlayKind::Chaining
- | InlayKind::GenericParamList
- | InlayKind::ClosingParenthesis
- | InlayKind::AdjustmentPostfix
- | InlayKind::Lifetime
- | InlayKind::ClosingBrace => position(line_index, inlay_hint.range.end()),
+ position: match inlay_hint.position {
+ ide::InlayHintPosition::Before => position(line_index, inlay_hint.range.start()),
+ ide::InlayHintPosition::After => position(line_index, inlay_hint.range.end()),
},
- padding_left: Some(match inlay_hint.kind {
- InlayKind::Type => !render_colons,
- InlayKind::Chaining | InlayKind::ClosingBrace => true,
- InlayKind::ClosingParenthesis
- | InlayKind::Discriminant
- | InlayKind::OpeningParenthesis
- | InlayKind::BindingMode
- | InlayKind::ClosureReturnType
- | InlayKind::GenericParamList
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::Lifetime
- | InlayKind::Parameter => false,
- }),
- padding_right: Some(match inlay_hint.kind {
- InlayKind::ClosingParenthesis
- | InlayKind::OpeningParenthesis
- | InlayKind::Chaining
- | InlayKind::ClosureReturnType
- | InlayKind::GenericParamList
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::Type
- | InlayKind::Discriminant
- | InlayKind::ClosingBrace => false,
- InlayKind::BindingMode => {
- matches!(&label, lsp_types::InlayHintLabel::String(s) if s != "&")
- }
- InlayKind::Parameter | InlayKind::Lifetime => true,
- }),
+ padding_left: Some(inlay_hint.pad_left),
+ padding_right: Some(inlay_hint.pad_right),
kind: match inlay_hint.kind {
InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER),
- InlayKind::ClosureReturnType | InlayKind::Type | InlayKind::Chaining => {
- Some(lsp_types::InlayHintKind::TYPE)
- }
- InlayKind::ClosingParenthesis
- | InlayKind::Discriminant
- | InlayKind::OpeningParenthesis
- | InlayKind::BindingMode
- | InlayKind::GenericParamList
- | InlayKind::Lifetime
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::ClosingBrace => None,
+ InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
+ _ => None,
},
- text_edits: None,
+ text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
data: None,
tooltip,
label,
@@ -580,6 +518,8 @@ pub(crate) fn semantic_tokens(
text: &str,
line_index: &LineIndex,
highlights: Vec<HlRange>,
+ semantics_tokens_augments_syntax_tokens: bool,
+ non_standard_tokens: bool,
) -> lsp_types::SemanticTokens {
let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
@@ -589,7 +529,35 @@ pub(crate) fn semantic_tokens(
continue;
}
- let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
+ if semantics_tokens_augments_syntax_tokens {
+ match highlight_range.highlight.tag {
+ HlTag::BoolLiteral
+ | HlTag::ByteLiteral
+ | HlTag::CharLiteral
+ | HlTag::Comment
+ | HlTag::Keyword
+ | HlTag::NumericLiteral
+ | HlTag::Operator(_)
+ | HlTag::Punctuation(_)
+ | HlTag::StringLiteral
+ | HlTag::None
+ if highlight_range.highlight.mods.is_empty() =>
+ {
+ continue
+ }
+ _ => (),
+ }
+ }
+
+ let (mut ty, mut mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
+
+ if !non_standard_tokens {
+ ty = match standard_fallback_type(ty) {
+ Some(ty) => ty,
+ None => continue,
+ };
+ mods.standard_fallback();
+ }
let token_index = semantic_tokens::type_index(ty);
let modifier_bitset = mods.0;
@@ -710,6 +678,7 @@ fn semantic_token_type_and_modifiers(
HlMod::Injected => semantic_tokens::INJECTED,
HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
HlMod::Library => semantic_tokens::LIBRARY,
+ HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
HlMod::Mutable => semantic_tokens::MUTABLE,
HlMod::Public => semantic_tokens::PUBLIC,
HlMod::Reference => semantic_tokens::REFERENCE,
@@ -1215,6 +1184,14 @@ pub(crate) fn code_lens(
data: None,
})
}
+ if lens_config.interpret {
+ let command = command::interpret_single(&r);
+ acc.push(lsp_types::CodeLens {
+ range: annotation_range,
+ command: Some(command),
+ data: None,
+ })
+ }
}
AnnotationKind::HasImpls { pos: file_range, data } => {
if !client_commands_config.show_reference {
@@ -1257,7 +1234,16 @@ pub(crate) fn code_lens(
acc.push(lsp_types::CodeLens {
range: annotation_range,
command,
- data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
+ data: (|| {
+ let version = snap.url_file_version(&url)?;
+ Some(
+ to_value(lsp_ext::CodeLensResolveData {
+ version,
+ kind: lsp_ext::CodeLensResolveDataKind::Impls(goto_params),
+ })
+ .unwrap(),
+ )
+ })(),
})
}
AnnotationKind::HasReferences { pos: file_range, data } => {
@@ -1287,7 +1273,16 @@ pub(crate) fn code_lens(
acc.push(lsp_types::CodeLens {
range: annotation_range,
command,
- data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
+ data: (|| {
+ let version = snap.url_file_version(&url)?;
+ Some(
+ to_value(lsp_ext::CodeLensResolveData {
+ version,
+ kind: lsp_ext::CodeLensResolveDataKind::References(doc_pos),
+ })
+ .unwrap(),
+ )
+ })(),
})
}
}
@@ -1341,6 +1336,15 @@ pub(crate) mod command {
}
}
+ pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command {
+ lsp_types::Command {
+ title: "Interpret".into(),
+ command: "rust-analyzer.interpretFunction".into(),
+ // FIXME: use the `_runnable` here.
+ arguments: Some(vec![]),
+ }
+ }
+
pub(crate) fn goto_location(
snap: &GlobalStateSnapshot,
nav: &NavigationTarget,
@@ -1406,9 +1410,9 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
#[cfg(test)]
mod tests {
- use std::sync::Arc;
-
- use ide::Analysis;
+ use ide::{Analysis, FilePosition};
+ use test_utils::extract_offset;
+ use triomphe::Arc;
use super::*;
@@ -1448,6 +1452,34 @@ fn main() {
}
}
+ #[test]
+ fn calling_function_with_ignored_code_in_signature() {
+ let text = r#"
+fn foo() {
+ bar($0);
+}
+/// ```
+/// # use crate::bar;
+/// bar(5);
+/// ```
+fn bar(_: usize) {}
+"#;
+
+ let (offset, text) = extract_offset(text);
+ let (analysis, file_id) = Analysis::from_single_file(text);
+ let help = signature_help(
+ analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(),
+ CallInfoConfig { params_only: false, docs: true },
+ false,
+ );
+ let docs = match &help.signatures[help.active_signature.unwrap() as usize].documentation {
+ Some(lsp_types::Documentation::MarkupContent(content)) => &content.value,
+ _ => panic!("documentation contains markup"),
+ };
+ assert!(docs.contains("bar(5)"));
+ assert!(!docs.contains("use crate::bar"));
+ }
+
// `Url` is not able to parse windows paths on unix machines.
#[test]
#[cfg(target_os = "windows")]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 587d64096..0bb29e708 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -59,7 +59,7 @@ use std::collections::Spam;
"#,
)
.with_config(serde_json::json!({
- "cargo": { "sysroot": "discover" }
+ "cargo": { "sysroot": "discover" },
}))
.server()
.wait_until_workspace_is_loaded();
@@ -508,7 +508,7 @@ fn main() {}
#[test]
fn test_missing_module_code_action_in_json_project() {
if skip_slow_tests() {
- // return;
+ return;
}
let tmp_dir = TestDir::new();
@@ -612,7 +612,7 @@ fn main() {{}}
"#
))
.with_config(serde_json::json!({
- "cargo": { "sysroot": "discover" }
+ "cargo": { "sysroot": "discover" },
}))
.server()
.wait_until_workspace_is_loaded();
@@ -685,7 +685,7 @@ version = \"0.0.0\"
#[test]
fn out_dirs_check() {
if skip_slow_tests() {
- // return;
+ return;
}
let server = Project::with_fixture(
@@ -711,10 +711,21 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs");
}
//- /src/main.rs
-#[rustc_builtin_macro] macro_rules! include {}
-#[rustc_builtin_macro] macro_rules! include_str {}
-#[rustc_builtin_macro] macro_rules! concat {}
-#[rustc_builtin_macro] macro_rules! env {}
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+#[rustc_builtin_macro] macro_rules! include {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! include_str {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! concat {
+ ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! env {
+ ($name:expr $(,)?) => {{ /* compiler built-in */ }};
+ ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
+}
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
@@ -741,6 +752,9 @@ fn main() {
"enable": true
},
"sysroot": null,
+ "extraEnv": {
+ "RUSTC_BOOTSTRAP": "1"
+ }
}
}))
.server()
@@ -749,7 +763,7 @@ fn main() {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(19, 10),
+ Position::new(30, 10),
),
work_done_progress_params: Default::default(),
});
@@ -758,7 +772,7 @@ fn main() {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(20, 10),
+ Position::new(31, 10),
),
work_done_progress_params: Default::default(),
});
@@ -768,23 +782,23 @@ fn main() {
GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(17, 9),
+ Position::new(28, 9),
),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
},
json!([{
"originSelectionRange": {
- "end": { "character": 10, "line": 17 },
- "start": { "character": 8, "line": 17 }
+ "end": { "character": 10, "line": 28 },
+ "start": { "character": 8, "line": 28 }
},
"targetRange": {
- "end": { "character": 9, "line": 8 },
- "start": { "character": 0, "line": 7 }
+ "end": { "character": 9, "line": 19 },
+ "start": { "character": 0, "line": 18 }
},
"targetSelectionRange": {
- "end": { "character": 8, "line": 8 },
- "start": { "character": 7, "line": 8 }
+ "end": { "character": 8, "line": 19 },
+ "start": { "character": 7, "line": 19 }
},
"targetUri": "file:///[..]src/main.rs"
}]),
@@ -794,23 +808,23 @@ fn main() {
GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(18, 9),
+ Position::new(29, 9),
),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
},
json!([{
"originSelectionRange": {
- "end": { "character": 10, "line": 18 },
- "start": { "character": 8, "line": 18 }
+ "end": { "character": 10, "line": 29 },
+ "start": { "character": 8, "line": 29 }
},
"targetRange": {
- "end": { "character": 9, "line": 12 },
- "start": { "character": 0, "line":11 }
+ "end": { "character": 9, "line": 23 },
+ "start": { "character": 0, "line": 22 }
},
"targetSelectionRange": {
- "end": { "character": 8, "line": 12 },
- "start": { "character": 7, "line": 12 }
+ "end": { "character": 8, "line": 23 },
+ "start": { "character": 7, "line": 23 }
},
"targetUri": "file:///[..]src/main.rs"
}]),
@@ -818,14 +832,24 @@ fn main() {
}
#[test]
-// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again
-#[cfg(any())]
+#[cfg(feature = "sysroot-abi")]
fn resolve_proc_macro() {
use expect_test::expect;
if skip_slow_tests() {
return;
}
+ // skip using the sysroot config as to prevent us from loading the sysroot sources
+ let mut rustc = std::process::Command::new(toolchain::rustc());
+ rustc.args(["--print", "sysroot"]);
+ let output = rustc.output().unwrap();
+ let sysroot =
+ vfs::AbsPathBuf::try_from(std::str::from_utf8(&output.stdout).unwrap().trim()).unwrap();
+
+ let standalone_server_name =
+ format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
+ let proc_macro_server_path = sysroot.join("libexec").join(&standalone_server_name);
+
let server = Project::with_fixture(
r###"
//- /foo/Cargo.toml
@@ -837,6 +861,7 @@ edition = "2021"
bar = {path = "../bar"}
//- /foo/src/main.rs
+#![feature(rustc_attrs, decl_macro)]
use bar::Bar;
#[rustc_builtin_macro]
@@ -902,7 +927,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
},
"procMacro": {
"enable": true,
- "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
+ "server": proc_macro_server_path.as_path().as_ref(),
}
}))
.root("foo")
@@ -913,7 +938,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("foo/src/main.rs"),
- Position::new(10, 9),
+ Position::new(11, 9),
),
work_done_progress_params: Default::default(),
});
@@ -1083,10 +1108,18 @@ version = "0.0.0"
//- /bar/src/lib.rs
pub fn bar() {}
+
+//- /baz/Cargo.toml
+[package]
+name = "baz"
+version = "0.0.0"
+
+//- /baz/src/lib.rs
"#,
)
.root("foo")
.root("bar")
+ .root("baz")
.with_config(json!({
"files": {
"excludeDirs": ["foo", "bar"]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 037fc89ac..b2a8041ae 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -9,11 +9,10 @@ use std::{
use crossbeam_channel::{after, select, Receiver};
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
-use project_model::ProjectManifest;
use rust_analyzer::{config::Config, lsp_ext, main_loop};
use serde::Serialize;
use serde_json::{json, to_string_pretty, Value};
-use test_utils::Fixture;
+use test_utils::FixtureWithProjectMeta;
use vfs::AbsPathBuf;
use crate::testdir::TestDir;
@@ -37,8 +36,12 @@ impl<'a> Project<'a> {
"sysroot": null,
// Can't use test binary as rustc wrapper.
"buildScripts": {
- "useRustcWrapper": false
+ "useRustcWrapper": false,
+ "enable": false,
},
+ },
+ "procMacro": {
+ "enable": false,
}
}),
}
@@ -80,10 +83,12 @@ impl<'a> Project<'a> {
profile::init_from(crate::PROFILE);
});
- let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture);
- assert!(proc_macros.is_empty());
+ let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(self.fixture);
+ assert!(proc_macro_names.is_empty());
assert!(mini_core.is_none());
- for entry in fixtures {
+ assert!(toolchain.is_none());
+ for entry in fixture {
let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
@@ -95,10 +100,6 @@ impl<'a> Project<'a> {
if roots.is_empty() {
roots.push(tmp_dir_path.clone());
}
- let discovered_projects = roots
- .into_iter()
- .map(|it| ProjectManifest::discover_single(&it).unwrap())
- .collect::<Vec<_>>();
let mut config = Config::new(
tmp_dir_path,
@@ -138,10 +139,10 @@ impl<'a> Project<'a> {
})),
..Default::default()
},
- Vec::new(),
+ roots,
);
- config.discovered_projects = Some(discovered_projects);
config.update(self.config).expect("invalid config");
+ config.rediscover_workspaces();
Server::new(tmp_dir, config)
}
@@ -154,7 +155,7 @@ pub(crate) fn project(fixture: &str) -> Server {
pub(crate) struct Server {
req_id: Cell<i32>,
messages: RefCell<Vec<Message>>,
- _thread: jod_thread::JoinHandle<()>,
+ _thread: stdx::thread::JoinHandle,
client: Connection,
/// XXX: remove the tempdir last
dir: TestDir,
@@ -164,7 +165,7 @@ impl Server {
fn new(dir: TestDir, config: Config) -> Server {
let (connection, client) = Connection::memory();
- let _thread = jod_thread::Builder::new()
+ let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("test server".to_string())
.spawn(move || main_loop(config, connection).unwrap())
.expect("failed to spawn a thread");
@@ -251,6 +252,9 @@ impl Server {
.clone()
.extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
.unwrap();
+ if status.health != lsp_ext::Health::Ok {
+ panic!("server errored/warned while loading workspace: {:?}", status.message);
+ }
status.quiescent
}
_ => false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 8e3097fce..f230cba2b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -257,6 +257,8 @@ fn check_dbg(path: &Path, text: &str) {
"ide-db/src/generated/lints.rs",
// test for doc test for remove_dbg
"src/tests/generated.rs",
+ // `expect!` string can contain `dbg!` (due to .dbg postfix)
+ "ide-completion/src/tests/special.rs",
];
if need_dbg.iter().any(|p| path.ends_with(p)) {
return;
diff --git a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
index 72d26635c..c5da6ceb4 100644
--- a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
@@ -58,21 +58,19 @@ impl CommentBlock {
assert!(tag.starts_with(char::is_uppercase));
let tag = format!("{tag}:");
- // Would be nice if we had `.retain_mut` here!
- CommentBlock::extract_untagged(text)
- .into_iter()
- .filter_map(|mut block| {
- let first = block.contents.remove(0);
- first.strip_prefix(&tag).map(|id| {
- if block.is_doc {
- panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}");
- }
+ let mut blocks = CommentBlock::extract_untagged(text);
+ blocks.retain_mut(|block| {
+ let first = block.contents.remove(0);
+ let Some(id) = first.strip_prefix(&tag) else { return false; };
+
+ if block.is_doc {
+ panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}");
+ }
- block.id = id.trim().to_string();
- block
- })
- })
- .collect()
+ block.id = id.trim().to_string();
+ true
+ });
+ blocks
}
pub fn extract_untagged(text: &str) -> Vec<CommentBlock> {
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index c881f2fd3..a67f36ae9 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -15,6 +15,8 @@ doctest = false
libc = "0.2.135"
backtrace = { version = "0.3.65", optional = true }
always-assert = { version = "0.1.2", features = ["log"] }
+jod-thread = "0.1.2"
+crossbeam-channel = "0.5.5"
# Think twice before adding anything here
[target.'cfg(windows)'.dependencies]
diff --git a/src/tools/rust-analyzer/crates/stdx/src/hash.rs b/src/tools/rust-analyzer/crates/stdx/src/hash.rs
deleted file mode 100644
index 0c21d2674..000000000
--- a/src/tools/rust-analyzer/crates/stdx/src/hash.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-//! A none hashing [`Hasher`] implementation.
-use std::{
- hash::{BuildHasher, Hasher},
- marker::PhantomData,
-};
-
-pub type NoHashHashMap<K, V> = std::collections::HashMap<K, V, NoHashHasherBuilder<K>>;
-pub type NoHashHashSet<K> = std::collections::HashSet<K, NoHashHasherBuilder<K>>;
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct NoHashHasherBuilder<T>(PhantomData<T>);
-
-impl<T> Default for NoHashHasherBuilder<T> {
- fn default() -> Self {
- Self(Default::default())
- }
-}
-
-pub trait NoHashHashable {}
-impl NoHashHashable for usize {}
-impl NoHashHashable for u32 {}
-
-pub struct NoHashHasher(u64);
-
-impl<T: NoHashHashable> BuildHasher for NoHashHasherBuilder<T> {
- type Hasher = NoHashHasher;
- fn build_hasher(&self) -> Self::Hasher {
- NoHashHasher(0)
- }
-}
-
-impl Hasher for NoHashHasher {
- fn finish(&self) -> u64 {
- self.0
- }
-
- fn write(&mut self, _: &[u8]) {
- unimplemented!("NoHashHasher should only be used for hashing primitive integers")
- }
-
- fn write_u8(&mut self, i: u8) {
- self.0 = i as u64;
- }
-
- fn write_u16(&mut self, i: u16) {
- self.0 = i as u64;
- }
-
- fn write_u32(&mut self, i: u32) {
- self.0 = i as u64;
- }
-
- fn write_u64(&mut self, i: u64) {
- self.0 = i;
- }
-
- fn write_usize(&mut self, i: usize) {
- self.0 = i as u64;
- }
-
- fn write_i8(&mut self, i: i8) {
- self.0 = i as u64;
- }
-
- fn write_i16(&mut self, i: i16) {
- self.0 = i as u64;
- }
-
- fn write_i32(&mut self, i: i32) {
- self.0 = i as u64;
- }
-
- fn write_i64(&mut self, i: i64) {
- self.0 = i as u64;
- }
-
- fn write_isize(&mut self, i: isize) {
- self.0 = i as u64;
- }
-}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
index 5639aaf57..24990d6a0 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
@@ -7,11 +7,11 @@ use std::process::Command;
use std::{cmp::Ordering, ops, time::Instant};
mod macros;
-pub mod hash;
pub mod process;
pub mod panic_context;
pub mod non_empty_vec;
pub mod rand;
+pub mod thread;
pub use always_assert::{always, never};
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs
new file mode 100644
index 000000000..e577eb431
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs
@@ -0,0 +1,102 @@
+//! A utility module for working with threads that automatically joins threads upon drop
+//! and abstracts over operating system quality of service (QoS) APIs
+//! through the concept of a “thread intent”.
+//!
+//! The intent of a thread is frozen at thread creation time,
+//! i.e. there is no API to change the intent of a thread once it has been spawned.
+//!
+//! As a system, rust-analyzer should have the property that
+//! old manual scheduling APIs are replaced entirely by QoS.
+//! To maintain this invariant, we panic when it is clear that
+//! old scheduling APIs have been used.
+//!
+//! Moreover, we also want to ensure that every thread has an intent set explicitly
+//! to force a decision about its importance to the system.
+//! Thus, [`ThreadIntent`] has no default value
+//! and every entry point to creating a thread requires a [`ThreadIntent`] upfront.
+
+use std::fmt;
+
+mod intent;
+mod pool;
+
+pub use intent::ThreadIntent;
+pub use pool::Pool;
+
+pub fn spawn<F, T>(intent: ThreadIntent, f: F) -> JoinHandle<T>
+where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+{
+ Builder::new(intent).spawn(f).expect("failed to spawn thread")
+}
+
+pub struct Builder {
+ intent: ThreadIntent,
+ inner: jod_thread::Builder,
+ allow_leak: bool,
+}
+
+impl Builder {
+ pub fn new(intent: ThreadIntent) -> Builder {
+ Builder { intent, inner: jod_thread::Builder::new(), allow_leak: false }
+ }
+
+ pub fn name(self, name: String) -> Builder {
+ Builder { inner: self.inner.name(name), ..self }
+ }
+
+ pub fn stack_size(self, size: usize) -> Builder {
+ Builder { inner: self.inner.stack_size(size), ..self }
+ }
+
+ pub fn allow_leak(self, b: bool) -> Builder {
+ Builder { allow_leak: b, ..self }
+ }
+
+ pub fn spawn<F, T>(self, f: F) -> std::io::Result<JoinHandle<T>>
+ where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+ {
+ let inner_handle = self.inner.spawn(move || {
+ self.intent.apply_to_current_thread();
+ f()
+ })?;
+
+ Ok(JoinHandle { inner: Some(inner_handle), allow_leak: self.allow_leak })
+ }
+}
+
+pub struct JoinHandle<T = ()> {
+ // `inner` is an `Option` so that we can
+ // take ownership of the contained `JoinHandle`.
+ inner: Option<jod_thread::JoinHandle<T>>,
+ allow_leak: bool,
+}
+
+impl<T> JoinHandle<T> {
+ pub fn join(mut self) -> T {
+ self.inner.take().unwrap().join()
+ }
+}
+
+impl<T> Drop for JoinHandle<T> {
+ fn drop(&mut self) {
+ if !self.allow_leak {
+ return;
+ }
+
+ if let Some(join_handle) = self.inner.take() {
+ join_handle.detach();
+ }
+ }
+}
+
+impl<T> fmt::Debug for JoinHandle<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("JoinHandle { .. }")
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs
new file mode 100644
index 000000000..7b65db30c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs
@@ -0,0 +1,287 @@
+//! An opaque façade around platform-specific QoS APIs.
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+// Please maintain order from least to most priority for the derived `Ord` impl.
+pub enum ThreadIntent {
+ /// Any thread which does work that isn’t in the critical path of the user typing
+ /// (e.g. processing Go To Definition).
+ Worker,
+
+ /// Any thread which does work caused by the user typing
+ /// (e.g. processing syntax highlighting).
+ LatencySensitive,
+}
+
+impl ThreadIntent {
+ // These APIs must remain private;
+ // we only want consumers to set thread intent
+ // either during thread creation or using our pool impl.
+
+ pub(super) fn apply_to_current_thread(self) {
+ let class = thread_intent_to_qos_class(self);
+ set_current_thread_qos_class(class);
+ }
+
+ pub(super) fn assert_is_used_on_current_thread(self) {
+ if IS_QOS_AVAILABLE {
+ let class = thread_intent_to_qos_class(self);
+ assert_eq!(get_current_thread_qos_class(), Some(class));
+ }
+ }
+}
+
+use imp::QoSClass;
+
+const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE;
+
+fn set_current_thread_qos_class(class: QoSClass) {
+ imp::set_current_thread_qos_class(class)
+}
+
+fn get_current_thread_qos_class() -> Option<QoSClass> {
+ imp::get_current_thread_qos_class()
+}
+
+fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass {
+ imp::thread_intent_to_qos_class(intent)
+}
+
+// All Apple platforms use XNU as their kernel
+// and thus have the concept of QoS.
+#[cfg(target_vendor = "apple")]
+mod imp {
+ use super::ThreadIntent;
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+ // Please maintain order from least to most priority for the derived `Ord` impl.
+ pub(super) enum QoSClass {
+ // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55
+ //
+ /// TLDR: invisible maintenance tasks
+ ///
+ /// Contract:
+ ///
+ /// * **You do not care about how long it takes for work to finish.**
+ /// * **You do not care about work being deferred temporarily.**
+ /// (e.g. if the device’s battery is in a critical state)
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// creating periodic backups of project files
+ /// * in a browser:
+ /// cleaning up cached sites which have not been accessed in a long time
+ /// * in a collaborative word processor:
+ /// creating a searchable index of all documents
+ ///
+ /// Use this QoS class for background tasks
+ /// which the user did not initiate themselves
+ /// and which are invisible to the user.
+ /// It is expected that this work will take significant time to complete:
+ /// minutes or even hours.
+ ///
+ /// This QoS class provides the most energy and thermally-efficient execution possible.
+ /// All other work is prioritized over background tasks.
+ Background,
+
+ /// TLDR: tasks that don’t block using your app
+ ///
+ /// Contract:
+ ///
+ /// * **Your app remains useful even as the task is executing.**
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// exporting a video to disk –
+ /// the user can still work on the timeline
+ /// * in a browser:
+ /// automatically extracting a downloaded zip file –
+ /// the user can still switch tabs
+ /// * in a collaborative word processor:
+ /// downloading images embedded in a document –
+ /// the user can still make edits
+ ///
+ /// Use this QoS class for tasks which
+ /// may or may not be initiated by the user,
+ /// but whose result is visible.
+ /// It is expected that this work will take a few seconds to a few minutes.
+ /// Typically your app will include a progress bar
+ /// for tasks using this class.
+ ///
+ /// This QoS class provides a balance between
+ /// performance, responsiveness and efficiency.
+ Utility,
+
+ /// TLDR: tasks that block using your app
+ ///
+ /// Contract:
+ ///
+ /// * **You need this work to complete
+ /// before the user can keep interacting with your app.**
+ /// * **Your work will not take more than a few seconds to complete.**
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// opening a saved project
+ /// * in a browser:
+ /// loading a list of the user’s bookmarks and top sites
+ /// when a new tab is created
+ /// * in a collaborative word processor:
+ /// running a search on the document’s content
+ ///
+ /// Use this QoS class for tasks which were initiated by the user
+ /// and block the usage of your app while they are in progress.
+ /// It is expected that this work will take a few seconds or less to complete;
+ /// not long enough to cause the user to switch to something else.
+ /// Your app will likely indicate progress on these tasks
+ /// through the display of placeholder content or modals.
+ ///
+ /// This QoS class is not energy-efficient.
+ /// Rather, it provides responsiveness
+ /// by prioritizing work above other tasks on the system
+ /// except for critical user-interactive work.
+ UserInitiated,
+
+ /// TLDR: render loops and nothing else
+ ///
+ /// Contract:
+ ///
+ /// * **You absolutely need this work to complete immediately
+ /// or your app will appear to freeze.**
+ /// * **Your work will always complete virtually instantaneously.**
+ ///
+ /// Examples:
+ ///
+ /// * the main thread in a GUI application
+ /// * the update & render loop in a game
+ /// * a secondary thread which progresses an animation
+ ///
+ /// Use this QoS class for any work which, if delayed,
+ /// will make your user interface unresponsive.
+ /// It is expected that this work will be virtually instantaneous.
+ ///
+ /// This QoS class is not energy-efficient.
+ /// Specifying this class is a request to run with
+ /// nearly all available system CPU and I/O bandwidth even under contention.
+ UserInteractive,
+ }
+
+ pub(super) const IS_QOS_AVAILABLE: bool = true;
+
+ pub(super) fn set_current_thread_qos_class(class: QoSClass) {
+ let c = match class {
+ QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
+ QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
+ QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
+ QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
+ };
+
+ let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) };
+
+ if code == 0 {
+ return;
+ }
+
+ let errno = unsafe { *libc::__error() };
+
+ match errno {
+ libc::EPERM => {
+ // This thread has been excluded from the QoS system
+ // due to a previous call to a function such as `pthread_setschedparam`
+ // which is incompatible with QoS.
+ //
+ // Panic instead of returning an error
+ // to maintain the invariant that we only use QoS APIs.
+ panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})")
+ }
+
+ libc::EINVAL => {
+ // This is returned if we pass something other than a qos_class_t
+ // to `pthread_set_qos_class_self_np`.
+ //
+ // This is impossible, so again panic.
+ unreachable!(
+ "invalid qos_class_t value was passed to pthread_set_qos_class_self_np"
+ )
+ }
+
+ _ => {
+ // `pthread_set_qos_class_self_np`’s documentation
+ // does not mention any other errors.
+ unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
+ }
+ }
+ }
+
+ pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
+ let current_thread = unsafe { libc::pthread_self() };
+ let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED;
+ let code = unsafe {
+ libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut())
+ };
+
+ if code != 0 {
+ // `pthread_get_qos_class_np`’s documentation states that
+ // an error value is placed into errno if the return code is not zero.
+ // However, it never states what errors are possible.
+ // Inspecting the source[0] shows that, as of this writing, it always returns zero.
+ //
+ // Whatever errors the function could report in future are likely to be
+ // ones which we cannot handle anyway
+ //
+ // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177
+ let errno = unsafe { *libc::__error() };
+ unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
+ }
+
+ match qos_class_raw {
+ libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
+ libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
+ libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set
+ libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
+ libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),
+
+ libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
+ // Using manual scheduling APIs causes threads to “opt out” of QoS.
+ // At this point they become incompatible with QoS,
+ // and as such have the “unspecified” QoS class.
+ //
+ // Panic instead of returning an error
+ // to maintain the invariant that we only use QoS APIs.
+ panic!("tried to get QoS of thread which has opted out of QoS")
+ }
+ }
+ }
+
+ pub(super) fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass {
+ match intent {
+ ThreadIntent::Worker => QoSClass::Utility,
+ ThreadIntent::LatencySensitive => QoSClass::UserInitiated,
+ }
+ }
+}
+
+// FIXME: Windows has QoS APIs, we should use them!
+#[cfg(not(target_vendor = "apple"))]
+mod imp {
+ use super::ThreadIntent;
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+ pub(super) enum QoSClass {
+ Default,
+ }
+
+ pub(super) const IS_QOS_AVAILABLE: bool = false;
+
+ pub(super) fn set_current_thread_qos_class(_: QoSClass) {}
+
+ pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
+ None
+ }
+
+ pub(super) fn thread_intent_to_qos_class(_: ThreadIntent) -> QoSClass {
+ QoSClass::Default
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
new file mode 100644
index 000000000..2ddd7da74
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
@@ -0,0 +1,92 @@
+//! [`Pool`] implements a basic custom thread pool
+//! inspired by the [`threadpool` crate](http://docs.rs/threadpool).
+//! When you spawn a task you specify a thread intent
+//! so the pool can schedule it to run on a thread with that intent.
+//! rust-analyzer uses this to prioritize work based on latency requirements.
+//!
+//! The thread pool is implemented entirely using
+//! the threading utilities in [`crate::thread`].
+
+use std::sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+};
+
+use crossbeam_channel::{Receiver, Sender};
+
+use super::{Builder, JoinHandle, ThreadIntent};
+
+pub struct Pool {
+ // `_handles` is never read: the field is present
+ // only for its `Drop` impl.
+
+ // The worker threads exit once the channel closes;
+ // make sure to keep `job_sender` above `handles`
+ // so that the channel is actually closed
+ // before we join the worker threads!
+ job_sender: Sender<Job>,
+ _handles: Vec<JoinHandle>,
+ extant_tasks: Arc<AtomicUsize>,
+}
+
+struct Job {
+ requested_intent: ThreadIntent,
+ f: Box<dyn FnOnce() + Send + 'static>,
+}
+
+impl Pool {
+ pub fn new(threads: usize) -> Pool {
+ const STACK_SIZE: usize = 8 * 1024 * 1024;
+ const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker;
+
+ let (job_sender, job_receiver) = crossbeam_channel::unbounded();
+ let extant_tasks = Arc::new(AtomicUsize::new(0));
+
+ let mut handles = Vec::with_capacity(threads);
+ for _ in 0..threads {
+ let handle = Builder::new(INITIAL_INTENT)
+ .stack_size(STACK_SIZE)
+ .name("Worker".into())
+ .spawn({
+ let extant_tasks = Arc::clone(&extant_tasks);
+ let job_receiver: Receiver<Job> = job_receiver.clone();
+ move || {
+ let mut current_intent = INITIAL_INTENT;
+ for job in job_receiver {
+ if job.requested_intent != current_intent {
+ job.requested_intent.apply_to_current_thread();
+ current_intent = job.requested_intent;
+ }
+ extant_tasks.fetch_add(1, Ordering::SeqCst);
+ (job.f)();
+ extant_tasks.fetch_sub(1, Ordering::SeqCst);
+ }
+ }
+ })
+ .expect("failed to spawn thread");
+
+ handles.push(handle);
+ }
+
+ Pool { _handles: handles, extant_tasks, job_sender }
+ }
+
+ pub fn spawn<F>(&self, intent: ThreadIntent, f: F)
+ where
+ F: FnOnce() + Send + 'static,
+ {
+ let f = Box::new(move || {
+ if cfg!(debug_assertions) {
+ intent.assert_is_used_on_current_thread();
+ }
+ f()
+ });
+
+ let job = Job { requested_intent: intent, f };
+ self.job_sender.send(job).unwrap();
+ }
+
+ pub fn len(&self) -> usize {
+ self.extant_tasks.load(Ordering::SeqCst)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 305cf2d39..fb38d25ab 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -16,12 +16,14 @@ doctest = false
cov-mark = "2.0.0-pre.1"
either = "1.7.0"
itertools = "0.10.5"
-rowan = "0.15.10"
-rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
+rowan = "0.15.11"
rustc-hash = "1.1.0"
once_cell = "1.17.0"
indexmap = "1.9.1"
-smol_str = "0.1.23"
+smol_str.workspace = true
+triomphe.workspace = true
+
+rustc_lexer.workspace = true
parser.workspace = true
profile.workspace = true
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 548b5ba8b..b096c9974 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -51,7 +51,9 @@ TypeArg =
Type
AssocTypeArg =
- NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg))
+ NameRef
+ (GenericArgList | ParamList RetType?)?
+ (':' TypeBoundList | ('=' Type | ConstArg))
LifetimeArg =
Lifetime
@@ -563,7 +565,7 @@ RefType =
'&' Lifetime? 'mut'? Type
ArrayType =
- '[' Type ';' Expr ']'
+ '[' Type ';' ConstArg ']'
SliceType =
'[' Type ']'
@@ -581,7 +583,7 @@ ImplTraitType =
'impl' TypeBoundList
DynTraitType =
- 'dyn' TypeBoundList
+ 'dyn'? TypeBoundList
TypeBoundList =
bounds:(TypeBound ('+' TypeBound)* '+'?)
@@ -613,7 +615,7 @@ Pat =
| ConstBlockPat
LiteralPat =
- Literal
+ '-'? Literal
IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)?
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index a493c92e7..b3ea6ca8d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -236,6 +236,21 @@ impl ast::GenericParamList {
}
}
+ /// Removes the existing generic param
+ pub fn remove_generic_param(&self, generic_param: ast::GenericParam) {
+ if let Some(previous) = generic_param.syntax().prev_sibling() {
+ if let Some(next_token) = previous.next_sibling_or_token() {
+ ted::remove_all(next_token..=generic_param.syntax().clone().into());
+ }
+ } else if let Some(next) = generic_param.syntax().next_sibling() {
+ if let Some(next_token) = next.prev_sibling_or_token() {
+ ted::remove_all(generic_param.syntax().clone().into()..=next_token);
+ }
+ } else {
+ ted::remove(generic_param.syntax());
+ }
+ }
+
/// Constructs a matching [`ast::GenericArgList`]
pub fn to_generic_args(&self) -> ast::GenericArgList {
let args = self.generic_params().filter_map(|param| match param {
@@ -465,6 +480,8 @@ impl ast::Impl {
}
impl ast::AssocItemList {
+ /// Attention! This function does align the first line of `item` with respect to `self`,
+ /// but it does _not_ change indentation of other lines (if any).
pub fn add_item(&self, item: ast::AssocItem) {
let (indent, position, whitespace) = match self.assoc_items().last() {
Some(last_item) => (
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index c43d0830b..36980b146 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -288,6 +288,7 @@ impl ast::ArrayExpr {
pub enum LiteralKind {
String(ast::String),
ByteString(ast::ByteString),
+ CString(ast::CString),
IntNumber(ast::IntNumber),
FloatNumber(ast::FloatNumber),
Char(ast::Char),
@@ -319,6 +320,9 @@ impl ast::Literal {
if let Some(t) = ast::ByteString::cast(token.clone()) {
return LiteralKind::ByteString(t);
}
+ if let Some(t) = ast::CString::cast(token.clone()) {
+ return LiteralKind::CString(t);
+ }
if let Some(t) = ast::Char::cast(token.clone()) {
return LiteralKind::Char(t);
}
@@ -366,8 +370,7 @@ impl ast::BlockExpr {
match parent.kind() {
FOR_EXPR | IF_EXPR => parent
.children()
- .filter(|it| ast::Expr::can_cast(it.kind()))
- .next()
+ .find(|it| ast::Expr::can_cast(it.kind()))
.map_or(true, |it| it == *self.syntax()),
LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false,
_ => true,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index fe3248453..e520801ea 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -121,6 +121,8 @@ impl ast::HasTypeBounds for AssocTypeArg {}
impl AssocTypeArg {
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
+ pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) }
+ pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
@@ -1205,7 +1207,7 @@ impl ArrayType {
pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
}
@@ -1375,6 +1377,7 @@ pub struct LiteralPat {
pub(crate) syntax: SyntaxNode,
}
impl LiteralPat {
+ pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
index a3209c5ab..f5863e9ef 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
@@ -91,6 +91,27 @@ impl AstToken for ByteString {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CString {
+ pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for CString {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.syntax, f)
+ }
+}
+impl AstToken for CString {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IntNumber {
pub(crate) syntax: SyntaxToken,
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 5aebe4cd9..3c2b7e56b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -158,34 +158,148 @@ fn ty_from_text(text: &str) -> ast::Type {
ast_from_text(&format!("type _T = {text};"))
}
+pub fn ty_alias(
+ ident: &str,
+ generic_param_list: Option<ast::GenericParamList>,
+ type_param_bounds: Option<ast::TypeParam>,
+ where_clause: Option<ast::WhereClause>,
+ assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
+) -> ast::TypeAlias {
+ let mut s = String::new();
+ s.push_str(&format!("type {}", ident));
+
+ if let Some(list) = generic_param_list {
+ s.push_str(&list.to_string());
+ }
+
+ if let Some(list) = type_param_bounds {
+ s.push_str(&format!(" : {}", &list));
+ }
+
+ if let Some(cl) = where_clause {
+ s.push_str(&format!(" {}", &cl.to_string()));
+ }
+
+ if let Some(exp) = assignment {
+ if let Some(cl) = exp.1 {
+ s.push_str(&format!(" = {} {}", &exp.0.to_string(), &cl.to_string()));
+ } else {
+ s.push_str(&format!(" = {}", &exp.0.to_string()));
+ }
+ }
+
+ s.push(';');
+ ast_from_text(&s)
+}
+
pub fn assoc_item_list() -> ast::AssocItemList {
ast_from_text("impl C for D {}")
}
-// FIXME: `ty_params` should be `ast::GenericArgList`
+fn merge_gen_params(
+ ps: Option<ast::GenericParamList>,
+ bs: Option<ast::GenericParamList>,
+) -> Option<ast::GenericParamList> {
+ match (ps, bs) {
+ (None, None) => None,
+ (None, Some(bs)) => Some(bs),
+ (Some(ps), None) => Some(ps),
+ (Some(ps), Some(bs)) => {
+ for b in bs.generic_params() {
+ ps.add_generic_param(b);
+ }
+ Some(ps)
+ }
+ }
+}
+
pub fn impl_(
- ty: ast::Path,
- params: Option<ast::GenericParamList>,
- ty_params: Option<ast::GenericParamList>,
+ generic_params: Option<ast::GenericParamList>,
+ generic_args: Option<ast::GenericParamList>,
+ path_type: ast::Type,
+ where_clause: Option<ast::WhereClause>,
+ body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
) -> ast::Impl {
- let params = match params {
- Some(params) => params.to_string(),
- None => String::new(),
+ let (gen_params, tr_gen_args) = match (generic_params, generic_args) {
+ (None, None) => (String::new(), String::new()),
+ (None, Some(args)) => (String::new(), args.to_generic_args().to_string()),
+ (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()),
+ (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) {
+ Some(merged) => (params.to_string(), merged.to_generic_args().to_string()),
+ None => (params.to_string(), String::new()),
+ },
};
- let ty_params = match ty_params {
- Some(params) => params.to_string(),
+
+ let where_clause = match where_clause {
+ Some(pr) => pr.to_string(),
+ None => " ".to_string(),
+ };
+
+ let body = match body {
+ Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
None => String::new(),
};
- ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}"))
+
+ ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body))
}
+// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs`
+// `add_generic_arg()` just like `add_generic_param()`
+// is implemented for `ast::GenericParamList`
pub fn impl_trait(
- trait_: ast::Path,
- ty: ast::Path,
- ty_params: Option<ast::GenericParamList>,
+ is_unsafe: bool,
+ trait_gen_params: Option<ast::GenericParamList>,
+ trait_gen_args: Option<ast::GenericParamList>,
+ type_gen_params: Option<ast::GenericParamList>,
+ type_gen_args: Option<ast::GenericParamList>,
+ is_negative: bool,
+ path_type: ast::Type,
+ ty: ast::Type,
+ trait_where_clause: Option<ast::WhereClause>,
+ ty_where_clause: Option<ast::WhereClause>,
+ body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
) -> ast::Impl {
- let ty_params = ty_params.map_or_else(String::new, |params| params.to_string());
- ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
+ let is_unsafe = if is_unsafe { "unsafe " } else { "" };
+ let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) {
+ Some(pars) => pars.to_generic_args().to_string(),
+ None => String::new(),
+ };
+
+ let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) {
+ Some(pars) => pars.to_generic_args().to_string(),
+ None => String::new(),
+ };
+
+ let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) {
+ Some(pars) => pars.to_string(),
+ None => String::new(),
+ };
+
+ let is_negative = if is_negative { "! " } else { "" };
+
+ let where_clause = match (ty_where_clause, trait_where_clause) {
+ (None, None) => " ".to_string(),
+ (None, Some(tr)) => format!("\n{}\n", tr).to_string(),
+ (Some(ty), None) => format!("\n{}\n", ty).to_string(),
+ (Some(ty), Some(tr)) => {
+ let updated = ty.clone_for_update();
+ tr.predicates().for_each(|p| {
+ ty.add_predicate(p);
+ });
+ format!("\n{}\n", updated).to_string()
+ }
+ };
+
+ let body = match body {
+ Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
+ None => String::new(),
+ };
+
+ ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body))
+}
+
+pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType {
+ ast_from_text(&format!("fn f(x: impl {bounds}) {{}}"))
}
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
@@ -355,7 +469,7 @@ pub fn hacky_block_expr(
format_to!(buf, " {t}\n")
} else if kind == SyntaxKind::WHITESPACE {
let content = t.text().trim_matches(|c| c != '\n');
- if content.len() >= 1 {
+ if !content.is_empty() {
format_to!(buf, "{}", &content[1..])
}
}
@@ -827,6 +941,8 @@ pub fn fn_(
body: ast::BlockExpr,
ret_type: Option<ast::RetType>,
is_async: bool,
+ is_const: bool,
+ is_unsafe: bool,
) -> ast::Fn {
let type_params = match type_params {
Some(type_params) => format!("{type_params}"),
@@ -846,12 +962,13 @@ pub fn fn_(
};
let async_literal = if is_async { "async " } else { "" };
+ let const_literal = if is_const { "const " } else { "" };
+ let unsafe_literal = if is_unsafe { "unsafe " } else { "" };
ast_from_text(&format!(
- "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
+ "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
))
}
-
pub fn struct_(
visibility: Option<ast::Visibility>,
strukt_name: ast::Name,
@@ -901,7 +1018,7 @@ pub mod tokens {
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
SourceFile::parse(
- "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n",
+ "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
)
});
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index 2cd312e7f..090eb89f4 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -145,6 +145,10 @@ impl QuoteOffsets {
}
pub trait IsString: AstToken {
+ const RAW_PREFIX: &'static str;
+ fn is_raw(&self) -> bool {
+ self.text().starts_with(Self::RAW_PREFIX)
+ }
fn quote_offsets(&self) -> Option<QuoteOffsets> {
let text = self.text();
let offsets = QuoteOffsets::new(text)?;
@@ -183,20 +187,18 @@ pub trait IsString: AstToken {
cb(text_range + offset, unescaped_char);
});
}
-}
-
-impl IsString for ast::String {}
-
-impl ast::String {
- pub fn is_raw(&self) -> bool {
- self.text().starts_with('r')
- }
- pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
+ fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
let contents_range = self.text_range_between_quotes()?;
assert!(TextRange::up_to(contents_range.len()).contains_range(range));
Some(range + contents_range.start())
}
+}
+impl IsString for ast::String {
+ const RAW_PREFIX: &'static str = "r";
+}
+
+impl ast::String {
pub fn value(&self) -> Option<Cow<'_, str>> {
if self.is_raw() {
let text = self.text();
@@ -235,13 +237,11 @@ impl ast::String {
}
}
-impl IsString for ast::ByteString {}
+impl IsString for ast::ByteString {
+ const RAW_PREFIX: &'static str = "br";
+}
impl ast::ByteString {
- pub fn is_raw(&self) -> bool {
- self.text().starts_with("br")
- }
-
pub fn value(&self) -> Option<Cow<'_, [u8]>> {
if self.is_raw() {
let text = self.text();
@@ -280,6 +280,49 @@ impl ast::ByteString {
}
}
+impl IsString for ast::CString {
+ const RAW_PREFIX: &'static str = "cr";
+}
+
+impl ast::CString {
+ pub fn value(&self) -> Option<Cow<'_, str>> {
+ if self.is_raw() {
+ let text = self.text();
+ let text =
+ &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
+ return Some(Cow::Borrowed(text));
+ }
+
+ let text = self.text();
+ let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
+
+ let mut buf = String::new();
+ let mut prev_end = 0;
+ let mut has_error = false;
+ unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
+ unescaped_char,
+ buf.capacity() == 0,
+ ) {
+ (Ok(c), false) => buf.push(c),
+ (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
+ prev_end = char_range.end
+ }
+ (Ok(c), true) => {
+ buf.reserve_exact(text.len());
+ buf.push_str(&text[..prev_end]);
+ buf.push(c);
+ }
+ (Err(_), _) => has_error = true,
+ });
+
+ match (has_error, buf.capacity() == 0) {
+ (true, _) => None,
+ (false, true) => Some(Cow::Borrowed(text)),
+ (false, false) => Some(Cow::Owned(buf)),
+ }
+ }
+}
+
impl ast::IntNumber {
pub fn radix(&self) -> Radix {
match self.text().get(..2).unwrap_or_default() {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 6f57cbad6..efbf87966 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -43,10 +43,11 @@ pub mod utils;
pub mod ted;
pub mod hacks;
-use std::{marker::PhantomData, sync::Arc};
+use std::marker::PhantomData;
use stdx::format_to;
use text_edit::Indel;
+use triomphe::Arc;
pub use crate::{
ast::{AstNode, AstToken},
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
index 701e6232d..45e591609 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
@@ -39,7 +39,7 @@ fn reparse_token(
let prev_token = root.covering_element(edit.delete).as_token()?.clone();
let prev_token_kind = prev_token.kind();
match prev_token_kind {
- WHITESPACE | COMMENT | IDENT | STRING => {
+ WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => {
if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
// removing a new line may extends previous token
let deleted_range = edit.delete - prev_token.text_range().start();
@@ -166,8 +166,8 @@ fn merge_errors(
}
res.extend(new_errors.into_iter().map(|new_err| {
// fighting borrow checker with a variable ;)
- let offseted_range = new_err.range() + range_before_reparse.start();
- new_err.with_range(offseted_range)
+ let offsetted_range = new_err.range() + range_before_reparse.start();
+ new_err.with_range(offsetted_range)
}));
res
}
@@ -408,7 +408,7 @@ enum Foo {
#[test]
fn reparse_str_token_with_error_fixed() {
- do_check(r#""unterinated$0$0"#, "\"", 12);
+ do_check(r#""unterminated$0$0"#, "\"", 13);
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index ccce71966..c5783b91a 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -71,7 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
],
contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"],
- literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
+ literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"],
tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
nodes: &[
"SOURCE_FILE",
@@ -199,6 +199,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"GENERIC_PARAM",
"LIFETIME_PARAM",
"TYPE_PARAM",
+ "RETURN_TYPE_ARG",
"CONST_PARAM",
"GENERIC_ARG_LIST",
"LIFETIME",
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 e954b5825..c49c5fa10 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
@@ -535,6 +535,7 @@ impl Field {
"!" => "excl",
"*" => "star",
"&" => "amp",
+ "-" => "minus",
"_" => "underscore",
"." => "dot",
".." => "dotdot",
@@ -572,10 +573,11 @@ impl Field {
fn lower(grammar: &Grammar) -> AstSrc {
let mut res = AstSrc {
- tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident"
- .split_ascii_whitespace()
- .map(|it| it.to_string())
- .collect::<Vec<_>>(),
+ tokens:
+ "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident"
+ .split_ascii_whitespace()
+ .map(|it| it.to_string())
+ .collect::<Vec<_>>(),
..Default::default()
};
diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
index 913b24d42..09c080c0c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
@@ -3,6 +3,7 @@
use std::{cmp::Ordering, fmt, ops};
use rowan::GreenToken;
+use smol_str::SmolStr;
pub struct TokenText<'a>(pub(crate) Repr<'a>);
@@ -47,6 +48,12 @@ impl From<TokenText<'_>> for String {
}
}
+impl From<TokenText<'_>> for SmolStr {
+ fn from(token_text: TokenText<'_>) -> Self {
+ SmolStr::new(token_text.as_str())
+ }
+}
+
impl PartialEq<&'_ str> for TokenText<'_> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index fb2381110..e0ec6a242 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -5,11 +5,11 @@
mod block;
use rowan::Direction;
-use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode};
+use rustc_lexer::unescape::{self, unescape_literal, Mode};
use crate::{
algo,
- ast::{self, HasAttrs, HasVisibility},
+ ast::{self, HasAttrs, HasVisibility, IsString},
match_ast, AstNode, SyntaxError,
SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
SyntaxNode, SyntaxToken, TextSize, T,
@@ -44,7 +44,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
errors
}
-fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
+fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) {
use unescape::EscapeError as EE;
#[rustfmt::skip]
@@ -103,12 +103,15 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
EE::UnicodeEscapeInByte => {
"Byte literals must not contain unicode escapes"
}
- EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
+ EE::NonAsciiCharInByte => {
"Byte literals must not contain non-ASCII characters"
}
+ EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped",
+ EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape",
+
};
- err_message
+ (err_message, err.is_fatal())
}
fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
@@ -121,9 +124,13 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
let text = token.text();
// FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205)
- let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
+ let mut push_err = |prefix_len, off, err: unescape::EscapeError| {
let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
- acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
+ let (message, is_err) = rustc_unescape_error_to_string(err);
+ // FIXME: Emit lexer warnings
+ if is_err {
+ acc.push(SyntaxError::new_at_offset(message, off));
+ }
};
match literal.kind() {
@@ -132,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
if let Some(without_quotes) = unquote(text, 1, '"') {
unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
if let Err(err) = char {
- push_err(1, (range.start, err));
+ push_err(1, range.start, err);
}
});
}
@@ -143,20 +150,39 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
if let Some(without_quotes) = unquote(text, 2, '"') {
unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
if let Err(err) = char {
- push_err(2, (range.start, err));
+ push_err(1, range.start, err);
+ }
+ });
+ }
+ }
+ }
+ ast::LiteralKind::CString(s) => {
+ if !s.is_raw() {
+ if let Some(without_quotes) = unquote(text, 2, '"') {
+ unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(1, range.start, err);
}
});
}
}
}
ast::LiteralKind::Char(_) => {
- if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
- push_err(1, e);
+ if let Some(without_quotes) = unquote(text, 1, '\'') {
+ unescape_literal(without_quotes, Mode::Char, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(1, range.start, err);
+ }
+ });
}
}
ast::LiteralKind::Byte(_) => {
- if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
- push_err(2, e);
+ if let Some(without_quotes) = unquote(text, 2, '\'') {
+ unescape_literal(without_quotes, Mode::Byte, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(2, range.start, err);
+ }
+ });
}
}
ast::LiteralKind::IntNumber(_)
@@ -175,14 +201,14 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
assert_eq!(
node.parent(),
pair.parent(),
- "\nunpaired curlys:\n{}\n{:#?}\n",
+ "\nunpaired curlies:\n{}\n{:#?}\n",
root.text(),
root,
);
assert!(
node.next_sibling_or_token().is_none()
&& pair.prev_sibling_or_token().is_none(),
- "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+ "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n",
node,
root.text(),
node,
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
index f977d23c4..13852aa78 100644
--- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
@@ -39,13 +39,13 @@
ast::Root::cast(self.syntax()).unwrap()
}
pub fn syntax(&self) -> SyntaxNodeRef {
- self.root.brroowed()
+ self.root.borrowed()
}
mp_tree(root),
);
assert!(
node.next_sibling().is_none() && pair.prev_sibling().is_none(),
- "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+ "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n",
node,
root.text(),
node.text(),
diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
index 92b1ef23e..2b5b6f495 100644
--- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
[dependencies]
# Avoid adding deps here, this crate is widely used in tests it should compile fast!
dissimilar = "1.0.4"
-text-size = "1.1.0"
+text-size.workspace = true
rustc-hash = "1.1.0"
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index cd1235fa6..602baed37 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -86,7 +86,14 @@ pub struct MiniCore {
valid_flags: Vec<String>,
}
-impl Fixture {
+pub struct FixtureWithProjectMeta {
+ pub fixture: Vec<Fixture>,
+ pub mini_core: Option<MiniCore>,
+ pub proc_macro_names: Vec<String>,
+ pub toolchain: Option<String>,
+}
+
+impl FixtureWithProjectMeta {
/// Parses text which looks like this:
///
/// ```not_rust
@@ -96,37 +103,41 @@ impl Fixture {
/// //- other meta
/// ```
///
- /// Fixture can also start with a proc_macros and minicore declaration(in that order):
+ /// Fixture can also start with a proc_macros and minicore declaration (in that order):
///
/// ```
+ /// //- toolchain: nightly
/// //- proc_macros: identity
/// //- minicore: sized
/// ```
///
- /// That will include predefined proc macros and a subset of `libcore` into the fixture, see
- /// `minicore.rs` for what's available.
- pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<String>, Vec<Fixture>) {
+ /// That will set toolchain to nightly and include predefined proc macros and a subset of
+ /// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain
+ /// defaults to stable.
+ pub fn parse(ra_fixture: &str) -> Self {
let fixture = trim_indent(ra_fixture);
let mut fixture = fixture.as_str();
+ let mut toolchain = None;
let mut mini_core = None;
let mut res: Vec<Fixture> = Vec::new();
- let mut test_proc_macros = vec![];
-
- if fixture.starts_with("//- proc_macros:") {
- let first_line = fixture.split_inclusive('\n').next().unwrap();
- test_proc_macros = first_line
- .strip_prefix("//- proc_macros:")
- .unwrap()
- .split(',')
- .map(|it| it.trim().to_string())
- .collect();
- fixture = &fixture[first_line.len()..];
+ let mut proc_macro_names = vec![];
+
+ if let Some(meta) = fixture.strip_prefix("//- toolchain:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ toolchain = Some(meta.trim().to_string());
+ fixture = remain;
+ }
+
+ if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ proc_macro_names = meta.split(',').map(|it| it.trim().to_string()).collect();
+ fixture = remain;
}
- if fixture.starts_with("//- minicore:") {
- let first_line = fixture.split_inclusive('\n').next().unwrap();
- mini_core = Some(MiniCore::parse(first_line));
- fixture = &fixture[first_line.len()..];
+ if let Some(meta) = fixture.strip_prefix("//- minicore:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ mini_core = Some(MiniCore::parse(meta));
+ fixture = remain;
}
let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
@@ -142,7 +153,7 @@ impl Fixture {
}
if line.starts_with("//-") {
- let meta = Fixture::parse_meta_line(line);
+ let meta = Self::parse_meta_line(line);
res.push(meta);
} else {
if line.starts_with("// ")
@@ -160,7 +171,7 @@ impl Fixture {
}
}
- (mini_core, test_proc_macros, res)
+ Self { fixture: res, mini_core, proc_macro_names, toolchain }
}
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -243,10 +254,19 @@ impl Fixture {
}
impl MiniCore {
+ const RAW_SOURCE: &str = include_str!("./minicore.rs");
+
fn has_flag(&self, flag: &str) -> bool {
self.activated_flags.iter().any(|it| it == flag)
}
+ pub fn from_flags<'a>(flags: impl IntoIterator<Item = &'a str>) -> Self {
+ MiniCore {
+ activated_flags: flags.into_iter().map(|x| x.to_owned()).collect(),
+ valid_flags: Vec::new(),
+ }
+ }
+
#[track_caller]
fn assert_valid_flag(&self, flag: &str) {
if !self.valid_flags.iter().any(|it| it == flag) {
@@ -257,8 +277,7 @@ impl MiniCore {
fn parse(line: &str) -> MiniCore {
let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
- let line = line.strip_prefix("//- minicore:").unwrap().trim();
- for entry in line.split(", ") {
+ for entry in line.trim().split(", ") {
if res.has_flag(entry) {
panic!("duplicate minicore flag: {entry:?}");
}
@@ -268,13 +287,21 @@ impl MiniCore {
res
}
+ pub fn available_flags() -> impl Iterator<Item = &'static str> {
+ let lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
+ lines
+ .map_while(|x| x.strip_prefix("//!"))
+ .skip_while(|line| !line.contains("Available flags:"))
+ .skip(1)
+ .map(|x| x.split_once(':').unwrap().0.trim())
+ }
+
/// Strips parts of minicore.rs which are flagged by inactive flags.
///
/// This is probably over-engineered to support flags dependencies.
pub fn source_code(mut self) -> String {
let mut buf = String::new();
- let raw_mini_core = include_str!("./minicore.rs");
- let mut lines = raw_mini_core.split_inclusive('\n');
+ let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
let mut implications = Vec::new();
@@ -360,6 +387,10 @@ impl MiniCore {
}
}
+ if !active_regions.is_empty() {
+ panic!("unclosed regions: {:?} Add an `endregion` comment", active_regions);
+ }
+
for flag in &self.valid_flags {
if !seen_regions.iter().any(|it| it == flag) {
panic!("unused minicore flag: {flag:?}");
@@ -372,7 +403,7 @@ impl MiniCore {
#[test]
#[should_panic]
fn parse_fixture_checks_further_indented_metadata() {
- Fixture::parse(
+ FixtureWithProjectMeta::parse(
r"
//- /lib.rs
mod bar;
@@ -386,15 +417,18 @@ fn parse_fixture_checks_further_indented_metadata() {
#[test]
fn parse_fixture_gets_full_meta() {
- let (mini_core, proc_macros, parsed) = Fixture::parse(
- r#"
+ let FixtureWithProjectMeta { fixture: parsed, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(
+ r#"
+//- toolchain: nightly
//- proc_macros: identity
//- minicore: coerce_unsized
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
mod m;
"#,
- );
- assert_eq!(proc_macros, vec!["identity".to_string()]);
+ );
+ assert_eq!(toolchain, Some("nightly".to_string()));
+ assert_eq!(proc_macro_names, vec!["identity".to_string()]);
assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
assert_eq!(1, parsed.len());
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
index a7a52e08e..fd3e68e2d 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
@@ -27,7 +27,7 @@ pub use rustc_hash::FxHashMap;
pub use crate::{
assert_linear::AssertLinear,
- fixture::{Fixture, MiniCore},
+ fixture::{Fixture, FixtureWithProjectMeta, MiniCore},
};
pub const CURSOR_MARKER: &str = "$0";
@@ -95,7 +95,7 @@ fn try_extract_range(text: &str) -> Option<(TextRange, String)> {
Some((TextRange::new(start, end), text))
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub enum RangeOrOffset {
Range(TextRange),
Offset(TextSize),
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index ca6de4061..266bc2391 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -11,6 +11,8 @@
//! add:
//! as_ref: sized
//! bool_impl: option, fn
+//! builtin_impls:
+//! cell: copy, drop
//! clone: sized
//! coerce_unsized: unsize
//! copy: clone
@@ -21,26 +23,34 @@
//! drop:
//! eq: sized
//! error: fmt
-//! fmt: result
+//! fmt: result, transmute, coerce_unsized
//! fn:
//! from: sized
//! future: pin
//! generator: pin
//! hash:
+//! include:
//! index: sized
//! infallible:
+//! int_impl: size_of, transmute
//! iterator: option
//! iterators: iterator, fn
+//! manually_drop: drop
//! non_zero:
-//! option:
+//! option: panic
//! ord: eq, option
+//! panic: fmt
+//! phantom_data:
//! pin:
+//! pointee:
//! range:
//! result:
//! send: sized
+//! size_of: sized
//! sized:
//! slice:
//! sync: sized
+//! transmute:
//! try: infallible
//! unsize: sized
@@ -106,6 +116,7 @@ pub mod marker {
impl<T: ?Sized> Copy for *const T {}
impl<T: ?Sized> Copy for *mut T {}
impl<T: ?Sized> Copy for &T {}
+ impl Copy for ! {}
}
// endregion:copy
@@ -113,6 +124,11 @@ pub mod marker {
#[lang = "tuple_trait"]
pub trait Tuple {}
// endregion:fn
+
+ // region:phantom_data
+ #[lang = "phantom_data"]
+ pub struct PhantomData<T: ?Sized>;
+ // endregion:phantom_data
}
// region:default
@@ -124,6 +140,27 @@ pub mod default {
#[rustc_builtin_macro(Default, attributes(default))]
pub macro Default($item:item) {}
// endregion:derive
+
+ // region:builtin_impls
+ macro_rules! impl_default {
+ ($v:literal; $($t:ty)*) => {
+ $(
+ impl const Default for $t {
+ fn default() -> Self {
+ $v
+ }
+ }
+ )*
+ }
+ }
+
+ impl_default! {
+ 0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
+ }
+ impl_default! {
+ 0.0; f32 f64
+ }
+ // endregion:builtin_impls
}
// endregion:default
@@ -134,15 +171,100 @@ pub mod hash {
pub trait Hash {
fn hash<H: Hasher>(&self, state: &mut H);
}
+
+ // region:derive
+ #[rustc_builtin_macro]
+ pub macro Hash($item:item) {}
+ // endregion:derive
}
// endregion:hash
+// region:cell
+pub mod cell {
+ use crate::mem;
+
+ #[lang = "unsafe_cell"]
+ pub struct UnsafeCell<T: ?Sized> {
+ value: T,
+ }
+
+ impl<T> UnsafeCell<T> {
+ pub const fn new(value: T) -> UnsafeCell<T> {
+ UnsafeCell { value }
+ }
+
+ pub const fn get(&self) -> *mut T {
+ self as *const UnsafeCell<T> as *const T as *mut T
+ }
+ }
+
+ pub struct Cell<T: ?Sized> {
+ value: UnsafeCell<T>,
+ }
+
+ impl<T> Cell<T> {
+ pub const fn new(value: T) -> Cell<T> {
+ Cell { value: UnsafeCell::new(value) }
+ }
+
+ pub fn set(&self, val: T) {
+ let old = self.replace(val);
+ mem::drop(old);
+ }
+
+ pub fn replace(&self, val: T) -> T {
+ mem::replace(unsafe { &mut *self.value.get() }, val)
+ }
+ }
+
+ impl<T: Copy> Cell<T> {
+ pub fn get(&self) -> T {
+ unsafe { *self.value.get() }
+ }
+ }
+}
+// endregion:cell
+
// region:clone
pub mod clone {
#[lang = "clone"]
pub trait Clone: Sized {
fn clone(&self) -> Self;
}
+
+ impl<T> Clone for &T {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ // region:builtin_impls
+ macro_rules! impl_clone {
+ ($($t:ty)*) => {
+ $(
+ impl const Clone for $t {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ )*
+ }
+ }
+
+ impl_clone! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+ f32 f64
+ bool char
+ }
+
+ impl Clone for ! {
+ fn clone(&self) {
+ *self
+ }
+ }
+ // endregion:builtin_impls
+
// region:derive
#[rustc_builtin_macro]
pub macro Clone($item:item) {}
@@ -181,10 +303,82 @@ pub mod convert {
}
// endregion:as_ref
// region:infallible
- pub enum Infallibe {}
+ pub enum Infallible {}
// endregion:infallible
}
+pub mod mem {
+ // region:drop
+ // region:manually_drop
+ #[lang = "manually_drop"]
+ #[repr(transparent)]
+ pub struct ManuallyDrop<T: ?Sized> {
+ value: T,
+ }
+
+ impl<T> ManuallyDrop<T> {
+ pub const fn new(value: T) -> ManuallyDrop<T> {
+ ManuallyDrop { value }
+ }
+ }
+
+ // region:deref
+ impl<T: ?Sized> crate::ops::Deref for ManuallyDrop<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.value
+ }
+ }
+ // endregion:deref
+
+ // endregion:manually_drop
+
+ pub fn drop<T>(_x: T) {}
+ pub const fn replace<T>(dest: &mut T, src: T) -> T {
+ unsafe {
+ let result = crate::ptr::read(dest);
+ crate::ptr::write(dest, src);
+ result
+ }
+ }
+ // endregion:drop
+
+ // region:transmute
+ extern "rust-intrinsic" {
+ pub fn transmute<Src, Dst>(src: Src) -> Dst;
+ }
+ // endregion:transmute
+
+ // region:size_of
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+ // endregion:size_of
+}
+
+pub mod ptr {
+ // region:drop
+ #[lang = "drop_in_place"]
+ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ unsafe { drop_in_place(to_drop) }
+ }
+ pub const unsafe fn read<T>(src: *const T) -> T {
+ *src
+ }
+ pub const unsafe fn write<T>(dst: *mut T, src: T) {
+ *dst = src;
+ }
+ // endregion:drop
+
+ // region:pointee
+ #[lang = "pointee_trait"]
+ pub trait Pointee {
+ #[lang = "metadata_type"]
+ type Metadata;
+ }
+ // endregion:pointee
+}
+
pub mod ops {
// region:coerce_unsized
mod unsize {
@@ -309,12 +503,6 @@ pub mod ops {
pub use self::index::{Index, IndexMut};
// endregion:index
- // region:drop
- pub mod mem {
- pub fn drop<T>(_x: T) {}
- }
- // endregion:drop
-
// region:range
mod range {
#[lang = "RangeFull"]
@@ -375,16 +563,82 @@ pub mod ops {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
+
+ mod impls {
+ use crate::marker::Tuple;
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const Fn<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call(&self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ type Output = F::Output;
+
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ type Output = F::Output;
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+ }
}
pub use self::function::{Fn, FnMut, FnOnce};
// endregion:fn
// region:try
mod try_ {
+ use super::super::convert::Infallible;
+
pub enum ControlFlow<B, C = ()> {
+ #[lang = "Continue"]
Continue(C),
+ #[lang = "Break"]
Break(B),
}
- pub trait FromResidual<R = Self::Residual> {
+ pub trait FromResidual<R = <Self as Try>::Residual> {
#[lang = "from_residual"]
fn from_residual(residual: R) -> Self;
}
@@ -400,14 +654,80 @@ pub mod ops {
impl<B, C> Try for ControlFlow<B, C> {
type Output = C;
- type Residual = ControlFlow<B, convert::Infallible>;
- fn from_output(output: Self::Output) -> Self {}
- fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
+ type Residual = ControlFlow<B, Infallible>;
+ fn from_output(output: Self::Output) -> Self {
+ ControlFlow::Continue(output)
+ }
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ ControlFlow::Continue(x) => ControlFlow::Continue(x),
+ ControlFlow::Break(x) => ControlFlow::Break(ControlFlow::Break(x)),
+ }
+ }
}
impl<B, C> FromResidual for ControlFlow<B, C> {
- fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
+ fn from_residual(residual: ControlFlow<B, Infallible>) -> Self {
+ match residual {
+ ControlFlow::Break(b) => ControlFlow::Break(b),
+ ControlFlow::Continue(_) => loop {},
+ }
+ }
}
+ // region:option
+ impl<T> Try for Option<T> {
+ type Output = T;
+ type Residual = Option<Infallible>;
+ fn from_output(output: Self::Output) -> Self {
+ Some(output)
+ }
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Some(x) => ControlFlow::Continue(x),
+ None => ControlFlow::Break(None),
+ }
+ }
+ }
+
+ impl<T> FromResidual for Option<T> {
+ fn from_residual(x: Option<Infallible>) -> Self {
+ match x {
+ None => None,
+ Some(_) => loop {},
+ }
+ }
+ }
+ // endregion:option
+ // region:result
+ // region:from
+ use super::super::convert::From;
+
+ impl<T, E> Try for Result<T, E> {
+ type Output = T;
+ type Residual = Result<Infallible, E>;
+
+ fn from_output(output: Self::Output) -> Self {
+ Ok(output)
+ }
+
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Ok(v) => ControlFlow::Continue(v),
+ Err(e) => ControlFlow::Break(Err(e)),
+ }
+ }
+ }
+
+ impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {
+ fn from_residual(residual: Result<Infallible, E>) -> Self {
+ match residual {
+ Err(e) => Err(From::from(e)),
+ Ok(_) => loop {},
+ }
+ }
+ }
+ // endregion:from
+ // endregion:result
}
pub use self::try_::{ControlFlow, FromResidual, Try};
// endregion:try
@@ -424,6 +744,19 @@ pub mod ops {
pub trait AddAssign<Rhs = Self> {
fn add_assign(&mut self, rhs: Rhs);
}
+
+ // region:builtin_impls
+ macro_rules! add_impl {
+ ($($t:ty)*) => ($(
+ impl const Add for $t {
+ type Output = $t;
+ fn add(self, other: $t) -> $t { self + other }
+ }
+ )*)
+ }
+
+ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
+ // endregion:builtin_impls
// endregion:add
// region:generator
@@ -499,12 +832,111 @@ pub mod fmt {
pub struct Error;
pub type Result = Result<(), Error>;
pub struct Formatter<'a>;
+ pub struct DebugTuple;
+ pub struct DebugStruct;
+ impl Formatter<'_> {
+ pub fn debug_tuple(&mut self, name: &str) -> DebugTuple {
+ DebugTuple
+ }
+
+ pub fn debug_struct(&mut self, name: &str) -> DebugStruct {
+ DebugStruct
+ }
+ }
+
+ impl DebugTuple {
+ pub fn field(&mut self, value: &dyn Debug) -> &mut Self {
+ self
+ }
+
+ pub fn finish(&mut self) -> Result {
+ Ok(())
+ }
+ }
+
+ impl DebugStruct {
+ pub fn field(&mut self, name: &str, value: &dyn Debug) -> &mut Self {
+ self
+ }
+
+ pub fn finish(&mut self) -> Result {
+ Ok(())
+ }
+ }
+
pub trait Debug {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
pub trait Display {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
+
+ extern "C" {
+ type Opaque;
+ }
+
+ #[lang = "format_argument"]
+ pub struct Argument<'a> {
+ value: &'a Opaque,
+ formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+ }
+
+ impl<'a> Argument<'a> {
+ pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+ use crate::mem::transmute;
+ unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ }
+ }
+
+ #[lang = "format_arguments"]
+ pub struct Arguments<'a> {
+ pieces: &'a [&'static str],
+ args: &'a [Argument<'a>],
+ }
+
+ impl<'a> Arguments<'a> {
+ pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments { pieces, args }
+ }
+ }
+
+ // region:derive
+ #[rustc_builtin_macro]
+ pub macro Debug($item:item) {}
+ // endregion:derive
+
+ // region:builtin_impls
+ macro_rules! impl_debug {
+ ($($t:ty)*) => {
+ $(
+ impl const Debug for $t {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Ok(())
+ }
+ }
+ )*
+ }
+ }
+
+ impl_debug! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+ f32 f64
+ bool char
+ }
+
+ impl<T: Debug> Debug for [T] {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Ok(())
+ }
+ }
+
+ impl<T: Debug + ?Sized> Debug for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ (&**self).fmt(f)
+ }
+ }
+ // endregion:builtin_impls
}
// endregion:fmt
@@ -537,12 +969,30 @@ pub mod option {
}
}
+ pub const fn as_ref(&self) -> Option<&T> {
+ match self {
+ Some(x) => Some(x),
+ None => None,
+ }
+ }
+
pub fn and<U>(self, optb: Option<U>) -> Option<U> {
loop {}
}
pub fn unwrap_or(self, default: T) -> T {
- loop {}
+ match self {
+ Some(val) => val,
+ None => default,
+ }
}
+ // region:result
+ pub const fn ok_or<E>(self, err: E) -> Result<T, E> {
+ match self {
+ Some(v) => Ok(v),
+ None => Err(err),
+ }
+ }
+ // endregion:result
// region:fn
pub fn and_then<U, F>(self, f: F) -> Option<U>
where
@@ -713,8 +1163,6 @@ pub mod iter {
mod traits {
mod iterator {
- use super::super::Take;
-
pub trait Iterator {
type Item;
#[lang = "next"]
@@ -764,12 +1212,19 @@ pub mod iter {
self
}
}
- pub struct IntoIter<T, const N: usize>([T; N]);
+ struct IndexRange {
+ start: usize,
+ end: usize,
+ }
+ pub struct IntoIter<T, const N: usize> {
+ data: [T; N],
+ range: IndexRange,
+ }
impl<T, const N: usize> IntoIterator for [T; N] {
type Item = T;
type IntoIter = IntoIter<T, N>;
fn into_iter(self) -> I {
- IntoIter(self)
+ IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
}
}
impl<T, const N: usize> Iterator for IntoIter<T, N> {
@@ -785,16 +1240,64 @@ pub mod iter {
}
// endregion:iterator
-// region:derive
+// region:panic
+mod panic {
+ pub macro panic_2021 {
+ ($($t:tt)+) => (
+ $crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
+ ),
+ }
+}
+
+mod panicking {
+ #[lang = "panic_fmt"]
+ pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! {
+ loop {}
+ }
+}
+// endregion:panic
+
mod macros {
+ // region:panic
+ #[macro_export]
+ #[rustc_builtin_macro(std_panic)]
+ macro_rules! panic {
+ ($($arg:tt)*) => {
+ /* compiler built-in */
+ };
+ }
+
+ pub(crate) use panic;
+ // endregion:panic
+
+ // region:fmt
+ #[macro_export]
+ #[rustc_builtin_macro]
+ macro_rules! const_format_args {
+ ($fmt:expr) => {{ /* compiler built-in */ }};
+ ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+ }
+
+ pub(crate) use const_format_args;
+ // endregion:fmt
+
+ // region:derive
pub(crate) mod builtin {
#[rustc_builtin_macro]
pub macro derive($item:item) {
/* compiler built-in */
}
}
+ // endregion:derive
+
+ // region:include
+ #[rustc_builtin_macro]
+ #[macro_export]
+ macro_rules! include {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+ }
+ // endregion:include
}
-// endregion:derive
// region:non_zero
pub mod num {
@@ -818,6 +1321,25 @@ impl bool {
}
// endregion:bool_impl
+// region:int_impl
+macro_rules! impl_int {
+ ($($t:ty)*) => {
+ $(
+ impl $t {
+ pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+ unsafe { mem::transmute(bytes) }
+ }
+ }
+ )*
+ }
+}
+
+impl_int! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+}
+// endregion:int_impl
+
// region:error
pub mod error {
#[rustc_has_incoherent_inherent_impls]
@@ -848,6 +1370,7 @@ pub mod prelude {
ops::Drop, // :drop
ops::{Fn, FnMut, FnOnce}, // :fn
option::Option::{self, None, Some}, // :option
+ panic, // :panic
result::Result::{self, Err, Ok}, // :result
};
}
diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
index 337cd2347..76d0ca5cc 100644
--- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
@@ -13,4 +13,4 @@ doctest = false
[dependencies]
itertools = "0.10.5"
-text-size = "1.1.0"
+text-size.workspace = true
diff --git a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
index 9bb4271b6..4705d1818 100644
--- a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
@@ -176,6 +176,7 @@ impl TextEditBuilder {
pub fn finish(self) -> TextEdit {
let mut indels = self.indels;
assert_disjoint_or_equal(&mut indels);
+ indels = coalesce_indels(indels);
TextEdit { indels }
}
pub fn invalidates_offset(&self, offset: TextSize) -> bool {
@@ -205,6 +206,21 @@ where
indels.clone().zip(indels.skip(1)).all(|(l, r)| l.delete.end() <= r.delete.start() || l == r)
}
+fn coalesce_indels(indels: Vec<Indel>) -> Vec<Indel> {
+ indels
+ .into_iter()
+ .coalesce(|mut a, b| {
+ if a.delete.end() == b.delete.start() {
+ a.insert.push_str(&b.insert);
+ a.delete = TextRange::new(a.delete.start(), b.delete.end());
+ Ok(a)
+ } else {
+ Err((a, b))
+ }
+ })
+ .collect_vec()
+}
+
#[cfg(test)]
mod tests {
use super::{TextEdit, TextEditBuilder, TextRange};
@@ -261,4 +277,40 @@ mod tests {
let edit2 = TextEdit::delete(range(9, 13));
assert!(edit1.union(edit2).is_err());
}
+
+ #[test]
+ fn test_coalesce_disjoint() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "aa".into());
+ builder.replace(range(5, 7), "bb".into());
+ let edit = builder.finish();
+
+ assert_eq!(edit.indels.len(), 2);
+ }
+
+ #[test]
+ fn test_coalesce_adjacent() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "aa".into());
+ builder.replace(range(3, 5), "bb".into());
+
+ let edit = builder.finish();
+ assert_eq!(edit.indels.len(), 1);
+ assert_eq!(edit.indels[0].insert, "aabb");
+ assert_eq!(edit.indels[0].delete, range(1, 5));
+ }
+
+ #[test]
+ fn test_coalesce_adjacent_series() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "au".into());
+ builder.replace(range(3, 5), "www".into());
+ builder.replace(range(5, 8), "".into());
+ builder.replace(range(8, 9), "ub".into());
+
+ let edit = builder.finish();
+ assert_eq!(edit.indels.len(), 1);
+ assert_eq!(edit.indels[0].insert, "auwwwub");
+ assert_eq!(edit.indels[0].delete, range(1, 9));
+ }
}
diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml
index b84693831..a28ee5f1c 100644
--- a/src/tools/rust-analyzer/crates/tt/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml
@@ -12,6 +12,6 @@ rust-version.workspace = true
doctest = false
[dependencies]
-smol_str = "0.1.23"
+smol_str.workspace = true
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index b7dbc82e1..c2ebf0374 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -153,6 +153,12 @@ pub struct Ident<Span> {
pub span: Span,
}
+impl<S> Ident<S> {
+ pub fn new(text: impl Into<SmolStr>, span: S) -> Self {
+ Ident { text: text.into(), span }
+ }
+}
+
fn print_debug_subtree<Span: fmt::Debug>(
f: &mut fmt::Formatter<'_>,
subtree: &Subtree<Span>,
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index e06b98d81..5d61a2272 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -13,10 +13,10 @@ doctest = false
[dependencies]
tracing = "0.1.35"
-jod-thread = "0.1.2"
walkdir = "2.3.2"
crossbeam-channel = "0.5.5"
notify = "5.0"
+stdx.workspace = true
vfs.workspace = true
paths.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
index c95304e55..abfc51dfe 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
@@ -21,7 +21,7 @@ use walkdir::WalkDir;
pub struct NotifyHandle {
// Relative order of fields below is significant.
sender: Sender<Message>,
- _thread: jod_thread::JoinHandle,
+ _thread: stdx::thread::JoinHandle,
}
#[derive(Debug)]
@@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle {
fn spawn(sender: loader::Sender) -> NotifyHandle {
let actor = NotifyActor::new(sender);
let (sender, receiver) = unbounded::<Message>();
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("VfsLoader".to_owned())
.spawn(move || actor.run(receiver))
.expect("failed to spawn thread");
diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
index 802a30006..3ae3dc83c 100644
--- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
rustc-hash = "1.1.0"
fst = "0.4.7"
indexmap = "1.9.1"
+nohash-hasher.workspace = true
paths.workspace = true
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
index 700aebe0b..0392ef3ce 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
@@ -5,8 +5,8 @@
use std::fmt;
use fst::{IntoStreamer, Streamer};
+use nohash_hasher::IntMap;
use rustc_hash::FxHashMap;
-use stdx::hash::NoHashHashMap;
use crate::{AnchoredPath, FileId, Vfs, VfsPath};
@@ -14,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath};
#[derive(Default, Clone, Eq, PartialEq)]
pub struct FileSet {
files: FxHashMap<VfsPath, FileId>,
- paths: NoHashHashMap<FileId, VfsPath>,
+ paths: IntMap<FileId, VfsPath>,
}
impl FileSet {
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index 14972d290..fe3dfe619 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -62,7 +62,8 @@ pub use paths::{AbsPath, AbsPathBuf};
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);
-impl stdx::hash::NoHashHashable for FileId {}
+/// safe because `FileId` is a newtype of `u32`
+impl nohash_hasher::IsEnabled for FileId {}
/// Storage for all files read by rust-analyzer.
///
@@ -108,13 +109,6 @@ pub enum ChangeKind {
}
impl Vfs {
- /// Amount of files currently stored.
- ///
- /// Note that this includes deleted files.
- pub fn len(&self) -> usize {
- self.data.len()
- }
-
/// Id of the given path if it exists in the `Vfs` and is not deleted.
pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
self.interner.get(path).filter(|&it| self.get(it).is_some())
@@ -139,6 +133,11 @@ impl Vfs {
self.get(file_id).as_deref().unwrap()
}
+ /// Returns the overall memory usage for the stored files.
+ pub fn memory_usage(&self) -> usize {
+ self.data.iter().flatten().map(|d| d.capacity()).sum()
+ }
+
/// Returns an iterator over the stored ids and their corresponding paths.
///
/// This will skip deleted files.
@@ -158,16 +157,18 @@ impl Vfs {
///
/// If the path does not currently exists in the `Vfs`, allocates a new
/// [`FileId`] for it.
- pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
+ pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option<Vec<u8>>) -> bool {
let file_id = self.alloc_file_id(path);
- let change_kind = match (&self.get(file_id), &contents) {
+ let change_kind = match (self.get(file_id), &contents) {
(None, None) => return false,
(Some(old), Some(new)) if old == new => return false,
(None, Some(_)) => ChangeKind::Create,
(Some(_), None) => ChangeKind::Delete,
(Some(_), Some(_)) => ChangeKind::Modify,
};
-
+ if let Some(contents) = &mut contents {
+ contents.shrink_to_fit();
+ }
*self.get_mut(file_id) = contents;
self.changes.push(ChangedFile { file_id, change_kind });
true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
index 38501a8ba..d327f2edf 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
@@ -107,10 +107,7 @@ impl VfsPath {
/// Returns `self`'s base name and file extension.
pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
match &self.0 {
- VfsPathRepr::PathBuf(p) => Some((
- p.file_stem()?.to_str()?,
- p.extension().and_then(|extension| extension.to_str()),
- )),
+ VfsPathRepr::PathBuf(p) => p.name_and_extension(),
VfsPathRepr::VirtualPath(p) => p.name_and_extension(),
}
}
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index de1422032..bc58aa722 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp_ext.rs hash: 37f31ae648632897
+lsp_ext.rs hash: 2d60bbffe70ae198
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
@@ -333,7 +333,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
### Unresolved Question
-* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
+* Should we return a nested brace structure, to allow [paredit](https://paredit.org/)-like actions of jump *out* of the current brace pair?
This is how `SelectionRange` request works.
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
@@ -386,14 +386,26 @@ rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look
## Open External Documentation
-This request is sent from client to server to get a URL to documentation for the symbol under the cursor, if available.
+This request is sent from the client to the server to obtain web and local URL(s) for documentation related to the symbol under the cursor, if available.
-**Method** `experimental/externalDocs`
+**Method:** `experimental/externalDocs`
-**Request:**: `TextDocumentPositionParams`
+**Request:** `TextDocumentPositionParams`
+
+**Response:** `string | null`
-**Response** `string | null`
+## Local Documentation
+**Experimental Client Capability:** `{ "localDocs": boolean }`
+
+If this capability is set, the `Open External Documentation` request returned from the server will have the following structure:
+
+```typescript
+interface ExternalDocsResponse {
+ web?: string;
+ local?: string;
+}
+```
## Analyzer Status
@@ -422,6 +434,16 @@ Returns internal status message, mostly for debugging purposes.
Reloads project information (that is, re-executes `cargo metadata`).
+## Rebuild proc-macros
+
+**Method:** `rust-analyzer/rebuildProcMacros`
+
+**Request:** `null`
+
+**Response:** `null`
+
+Rebuilds build scripts and proc-macros, and runs the build scripts to reseed the build data.
+
## Server Status
**Experimental Client Capability:** `{ "serverStatusNotification": boolean }`
@@ -538,6 +560,18 @@ For debugging or when working on rust-analyzer itself.
Returns a textual representation of the MIR of the function containing the cursor.
For debugging or when working on rust-analyzer itself.
+## Interpret Function
+
+**Method:** `rust-analyzer/interpretFunction`
+
+**Request:** `TextDocumentPositionParams`
+
+**Response:** `string`
+
+Tries to evaluate the function using internal rust analyzer knowledge, without compiling
+the code. Currently evaluates the function under cursor, but will give a runnable in
+future. Highly experimental.
+
## View File Text
**Method:** `rust-analyzer/viewFileText`
@@ -829,3 +863,26 @@ export interface Diagnostic {
rendered?: string;
};
}
+```
+
+## Dependency Tree
+
+**Method:** `rust-analyzer/fetchDependencyList`
+
+**Request:**
+
+```typescript
+export interface FetchDependencyListParams {}
+```
+
+**Response:**
+```typescript
+export interface FetchDependencyListResult {
+ crates: {
+ name: string;
+ version: string;
+ path: string;
+ }[];
+}
+```
+Returns all crates from this workspace, so it can be used create a viewTree to help navigate the dependency tree.
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 6937a7ed9..ea00c9540 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -71,6 +71,11 @@ cargo check --quiet --workspace --message-format=json --all-targets
Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
avoid checking unnecessary things.
--
+[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs (default: `{}`)::
++
+--
+List of cfg options to enable with the given values.
+--
[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`)::
+
--
@@ -120,7 +125,7 @@ Compilation target override (target triple).
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
+
--
-Unsets `#[cfg(test)]` for the specified crates.
+Unsets the implicit `#[cfg(test)]` for the specified crates.
--
[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`)::
+
@@ -352,6 +357,11 @@ Controls file watching implementation.
--
Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
--
+[[rust-analyzer.highlightRelated.closureCaptures.enable]]rust-analyzer.highlightRelated.closureCaptures.enable (default: `true`)::
++
+--
+Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
+--
[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`)::
+
--
@@ -416,7 +426,32 @@ Whether to show keyword hover popups. Only applies when
[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`)::
+
--
-Use markdown syntax for links in hover.
+Use markdown syntax for links on hover.
+--
+[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`)::
++
+--
+How to render the align information in a memory layout hover.
+--
+[[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`)::
++
+--
+Whether to show memory layout data on hover.
+--
+[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`)::
++
+--
+How to render the niche information in a memory layout hover.
+--
+[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`)::
++
+--
+How to render the offset information in a memory layout hover.
+--
+[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`)::
++
+--
+How to render the size information in a memory layout hover.
--
[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`)::
+
@@ -469,11 +504,21 @@ Whether to show inlay hints after a closing `}` to indicate what item it belongs
Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
to always show them).
--
+[[rust-analyzer.inlayHints.closureCaptureHints.enable]]rust-analyzer.inlayHints.closureCaptureHints.enable (default: `false`)::
++
+--
+Whether to show inlay hints for closure captures.
+--
[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`)::
+
--
Whether to show inlay type hints for return types of closures.
--
+[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`)::
++
+--
+Closure notation in type and chaining inlay hints.
+--
[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`)::
+
--
@@ -639,6 +684,11 @@ Elements must be paths pointing to `Cargo.toml`,
--
Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
--
+[[rust-analyzer.lru.query.capacities]]rust-analyzer.lru.query.capacities (default: `{}`)::
++
+--
+Sets the LRU capacity of the specified queries.
+--
[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
+
--
@@ -669,8 +719,7 @@ This config takes a map of crate names with the exported proc-macro names to ign
[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
+
--
-Internal config, path to proc-macro server executable (typically,
-this is rust-analyzer itself, but we override this in tests).
+Internal config, path to proc-macro server executable.
--
[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`)::
+
@@ -729,6 +778,11 @@ Inject additional highlighting into doc comments.
When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
doc links.
--
+[[rust-analyzer.semanticHighlighting.nonStandardTokens]]rust-analyzer.semanticHighlighting.nonStandardTokens (default: `true`)::
++
+--
+Whether the server is allowed to emit non-standard tokens and modifiers.
+--
[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`)::
+
--
@@ -748,7 +802,7 @@ of the generic `operator` token type.
[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`)::
+
--
-Use semantic tokens for punctuations.
+Use semantic tokens for punctuation.
When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
they are tagged with modifiers or have a special role.
@@ -762,7 +816,7 @@ calls.
[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`)::
+
--
-Use specialized semantic tokens for punctuations.
+Use specialized semantic tokens for punctuation.
When enabled, rust-analyzer will emit special token types for punctuation tokens instead
of the generic `punctuation` token type.
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index cb96feeb5..b5c095fd9 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -172,7 +172,7 @@ $ cargo xtask install --server
If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue].
On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
-==== `rustup`
+==== rustup
`rust-analyzer` is available in `rustup`:
@@ -181,19 +181,6 @@ On Unix, running the editor from a shell or changing the `.desktop` file to set
$ rustup component add rust-analyzer
----
-However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using:
-[source,bash]
-----
-$ rustup which --toolchain stable rust-analyzer
-----
-You can link to there from `~/.cargo/bin` or configure your editor to use the full path.
-
-Alternatively you might be able to configure your editor to start `rust-analyzer` using the command:
-[source,bash]
-----
-$ rustup run stable rust-analyzer
-----
-
==== Arch Linux
The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository):
@@ -257,7 +244,7 @@ Any other tools or libraries you will need to acquire from Flatpak.
Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-To use `rust-analyzer`, you need to install and enable one of the two popular two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available.
+To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available.
==== Eglot
@@ -589,7 +576,7 @@ Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** fea
If the date is more than a week ago, it's better to update rust-analyzer version.
The next thing to check would be panic messages in rust-analyzer's log.
-Log messages are printed to stderr, in VS Code you can see then in the `Output > Rust Analyzer Language Server` tab of the panel.
+Log messages are printed to stderr, in VS Code you can see them in the `Output > Rust Analyzer Language Server` tab of the panel.
To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted.
To fully capture LSP messages between the editor and the server, set `"rust-analyzer.trace.server": "verbose"` config and check
@@ -666,9 +653,28 @@ However, if you use some other build system, you'll have to describe the structu
[source,TypeScript]
----
interface JsonProject {
+ /// Path to the sysroot directory.
+ ///
+ /// The sysroot is where rustc looks for the
+ /// crates that are built-in to rust, such as
+ /// std.
+ ///
+ /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root
+ ///
+ /// To see the current value of sysroot, you
+ /// can query rustc:
+ ///
+ /// ```
+ /// $ rustc --print sysroot
+ /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin
+ /// ```
+ sysroot?: string;
/// Path to the directory with *source code* of
/// sysroot crates.
///
+ /// By default, this is `lib/rustlib/src/rust/library`
+ /// relative to the sysroot.
+ ///
/// It should point to the directory where std,
/// core, and friends can be found:
///
diff --git a/src/tools/rust-analyzer/lib/README.md b/src/tools/rust-analyzer/lib/README.md
index 6b2eeac2c..ed55e31d6 100644
--- a/src/tools/rust-analyzer/lib/README.md
+++ b/src/tools/rust-analyzer/lib/README.md
@@ -1,2 +1,5 @@
-Crates in this directory are published to crates.io and obey semver.
-They *could* live in a separate repo, but we want to experiment with a monorepo setup.
+# lib
+
+Crates in this directory are published to [crates.io](https://crates.io) and obey semver.
+
+They _could_ live in a separate repo, but we want to experiment with a monorepo setup.
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
index ccaaf3991..f39c3a3e4 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
@@ -4,8 +4,9 @@
#![warn(missing_docs)]
use std::{
- fmt,
+ cmp, fmt,
hash::{Hash, Hasher},
+ iter::{Enumerate, FusedIterator},
marker::PhantomData,
ops::{Index, IndexMut, Range, RangeInclusive},
};
@@ -17,13 +18,27 @@ pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawIdx(u32);
+impl RawIdx {
+ /// Constructs a [`RawIdx`] from a u32.
+ pub const fn from_u32(u32: u32) -> Self {
+ RawIdx(u32)
+ }
+
+ /// Deconstructs a [`RawIdx`] into the underlying u32.
+ pub const fn into_u32(self) -> u32 {
+ self.0
+ }
+}
+
impl From<RawIdx> for u32 {
+ #[inline]
fn from(raw: RawIdx) -> u32 {
raw.0
}
}
impl From<u32> for RawIdx {
+ #[inline]
fn from(idx: u32) -> RawIdx {
RawIdx(idx)
}
@@ -47,6 +62,18 @@ pub struct Idx<T> {
_ty: PhantomData<fn() -> T>,
}
+impl<T> Ord for Idx<T> {
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ self.raw.cmp(&other.raw)
+ }
+}
+
+impl<T> PartialOrd for Idx<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ self.raw.partial_cmp(&other.raw)
+ }
+}
+
impl<T> Clone for Idx<T> {
fn clone(&self) -> Self {
*self
@@ -79,12 +106,12 @@ impl<T> fmt::Debug for Idx<T> {
impl<T> Idx<T> {
/// Creates a new index from a [`RawIdx`].
- pub fn from_raw(raw: RawIdx) -> Self {
+ pub const fn from_raw(raw: RawIdx) -> Self {
Idx { raw, _ty: PhantomData }
}
/// Converts this index into the underlying [`RawIdx`].
- pub fn into_raw(self) -> RawIdx {
+ pub const fn into_raw(self) -> RawIdx {
self.raw
}
}
@@ -147,13 +174,46 @@ impl<T> IdxRange<T> {
pub fn is_empty(&self) -> bool {
self.range.is_empty()
}
+
+ /// Returns the start of the index range.
+ pub fn start(&self) -> Idx<T> {
+ Idx::from_raw(RawIdx::from(self.range.start))
+ }
+
+ /// Returns the end of the index range.
+ pub fn end(&self) -> Idx<T> {
+ Idx::from_raw(RawIdx::from(self.range.end))
+ }
}
impl<T> Iterator for IdxRange<T> {
type Item = Idx<T>;
+
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|raw| Idx::from_raw(raw.into()))
}
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.range.size_hint()
+ }
+
+ fn count(self) -> usize
+ where
+ Self: Sized,
+ {
+ self.range.count()
+ }
+
+ fn last(self) -> Option<Self::Item>
+ where
+ Self: Sized,
+ {
+ self.range.last().map(|raw| Idx::from_raw(raw.into()))
+ }
+
+ fn nth(&mut self, n: usize) -> Option<Self::Item> {
+ self.range.nth(n).map(|raw| Idx::from_raw(raw.into()))
+ }
}
impl<T> DoubleEndedIterator for IdxRange<T> {
@@ -162,6 +222,10 @@ impl<T> DoubleEndedIterator for IdxRange<T> {
}
}
+impl<T> ExactSizeIterator for IdxRange<T> {}
+
+impl<T> FusedIterator for IdxRange<T> {}
+
impl<T> fmt::Debug for IdxRange<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(&format!("IdxRange::<{}>", std::any::type_name::<T>()))
@@ -280,6 +344,21 @@ impl<T> Arena<T> {
idx
}
+ /// Densely allocates multiple values, returning the values’ index range.
+ ///
+ /// ```
+ /// let mut arena = la_arena::Arena::new();
+ /// let range = arena.alloc_many(0..4);
+ ///
+ /// assert_eq!(arena[range], [0, 1, 2, 3]);
+ /// ```
+ pub fn alloc_many<II: IntoIterator<Item = T>>(&mut self, iter: II) -> IdxRange<T> {
+ let start = self.next_idx();
+ self.extend(iter);
+ let end = self.next_idx();
+ IdxRange::new(start..end)
+ }
+
/// Returns an iterator over the arena’s elements.
///
/// ```
@@ -295,7 +374,7 @@ impl<T> Arena<T> {
/// ```
pub fn iter(
&self,
- ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
+ ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone {
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
}
@@ -335,7 +414,7 @@ impl<T> Arena<T> {
/// assert_eq!(iterator.next(), Some(&40));
/// assert_eq!(iterator.next(), Some(&60));
/// ```
- pub fn values(&mut self) -> impl Iterator<Item = &T> + ExactSizeIterator + DoubleEndedIterator {
+ pub fn values(&self) -> impl Iterator<Item = &T> + ExactSizeIterator + DoubleEndedIterator {
self.data.iter()
}
@@ -372,6 +451,12 @@ impl<T> Arena<T> {
}
}
+impl<T> AsMut<[T]> for Arena<T> {
+ fn as_mut(&mut self) -> &mut [T] {
+ self.data.as_mut()
+ }
+}
+
impl<T> Default for Arena<T> {
fn default() -> Arena<T> {
Arena { data: Vec::new() }
@@ -410,3 +495,32 @@ impl<T> FromIterator<T> for Arena<T> {
Arena { data: Vec::from_iter(iter) }
}
}
+
+/// An iterator over the arena’s elements.
+pub struct IntoIter<T>(Enumerate<<Vec<T> as IntoIterator>::IntoIter>);
+
+impl<T> Iterator for IntoIter<T> {
+ type Item = (Idx<T>, T);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
+ }
+}
+
+impl<T> IntoIterator for Arena<T> {
+ type Item = (Idx<T>, T);
+
+ type IntoIter = IntoIter<T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ IntoIter(self.data.into_iter().enumerate())
+ }
+}
+
+impl<T> Extend<T> for Arena<T> {
+ fn extend<II: IntoIterator<Item = T>>(&mut self, iter: II) {
+ for t in iter {
+ self.alloc(t);
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
index 7fff2b09c..750f345b5 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
@@ -1,3 +1,4 @@
+use std::iter::Enumerate;
use std::marker::PhantomData;
use crate::Idx;
@@ -72,17 +73,17 @@ impl<T, V> ArenaMap<Idx<T>, V> {
}
/// Returns an iterator over the values in the map.
- pub fn values(&self) -> impl Iterator<Item = &V> {
+ pub fn values(&self) -> impl Iterator<Item = &V> + DoubleEndedIterator {
self.v.iter().filter_map(|o| o.as_ref())
}
/// Returns an iterator over mutable references to the values in the map.
- pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
+ pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> + DoubleEndedIterator {
self.v.iter_mut().filter_map(|o| o.as_mut())
}
/// Returns an iterator over the arena indexes and values in the map.
- pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> {
+ pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> + DoubleEndedIterator {
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
}
@@ -94,12 +95,6 @@ impl<T, V> ArenaMap<Idx<T>, V> {
.filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
}
- /// Returns an iterator over the arena indexes and values in the map.
- // FIXME: Implement `IntoIterator` trait.
- pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> {
- self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?)))
- }
-
/// Gets the given key's corresponding entry in the map for in-place manipulation.
pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
let idx = Self::to_idx(idx);
@@ -154,6 +149,63 @@ impl<T, V> FromIterator<(Idx<V>, T)> for ArenaMap<Idx<V>, T> {
}
}
+pub struct ArenaMapIter<IDX, V> {
+ iter: Enumerate<std::vec::IntoIter<Option<V>>>,
+ _ty: PhantomData<IDX>,
+}
+
+impl<T, V> IntoIterator for ArenaMap<Idx<T>, V> {
+ type Item = (Idx<T>, V);
+
+ type IntoIter = ArenaMapIter<Idx<T>, V>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ let iter = self.v.into_iter().enumerate();
+ Self::IntoIter { iter, _ty: PhantomData }
+ }
+}
+
+impl<T, V> ArenaMapIter<Idx<T>, V> {
+ fn mapper((idx, o): (usize, Option<V>)) -> Option<(Idx<T>, V)> {
+ Some((ArenaMap::<Idx<T>, V>::from_idx(idx), o?))
+ }
+}
+
+impl<T, V> Iterator for ArenaMapIter<Idx<T>, V> {
+ type Item = (Idx<T>, V);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ for next in self.iter.by_ref() {
+ match Self::mapper(next) {
+ Some(r) => return Some(r),
+ None => continue,
+ }
+ }
+
+ None
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl<T, V> DoubleEndedIterator for ArenaMapIter<Idx<T>, V> {
+ #[inline]
+ fn next_back(&mut self) -> Option<Self::Item> {
+ while let Some(next_back) = self.iter.next_back() {
+ match Self::mapper(next_back) {
+ Some(r) => return Some(r),
+ None => continue,
+ }
+ }
+
+ None
+ }
+}
+
/// A view into a single entry in a map, which may either be vacant or occupied.
///
/// This `enum` is constructed from the [`entry`] method on [`ArenaMap`].
diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
new file mode 100644
index 000000000..019ad3a53
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "line-index"
+version = "0.1.0-pre.1"
+description = "Maps flat `TextSize` offsets to/from `(line, column)` representation."
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index"
+edition = "2021"
+
+[dependencies]
+text-size.workspace = true
+nohash-hasher.workspace = true
diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
new file mode 100644
index 000000000..ad67d3f24
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
@@ -0,0 +1,237 @@
+//! See [`LineIndex`].
+
+#![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)]
+
+#[cfg(test)]
+mod tests;
+
+use nohash_hasher::IntMap;
+
+pub use text_size::{TextRange, TextSize};
+
+/// `(line, column)` information in the native, UTF-8 encoding.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct LineCol {
+ /// Zero-based.
+ pub line: u32,
+ /// Zero-based UTF-8 offset.
+ pub col: u32,
+}
+
+/// A kind of wide character encoding.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub enum WideEncoding {
+ /// UTF-16.
+ Utf16,
+ /// UTF-32.
+ Utf32,
+}
+
+impl WideEncoding {
+ /// Returns the number of code units it takes to encode `text` in this encoding.
+ pub fn measure(&self, text: &str) -> usize {
+ match self {
+ WideEncoding::Utf16 => text.encode_utf16().count(),
+ WideEncoding::Utf32 => text.chars().count(),
+ }
+ }
+}
+
+/// `(line, column)` information in wide encodings.
+///
+/// See [`WideEncoding`] for the kinds of wide encodings available.
+//
+// Deliberately not a generic type and different from `LineCol`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct WideLineCol {
+ /// Zero-based.
+ pub line: u32,
+ /// Zero-based.
+ pub col: u32,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct WideChar {
+ /// Start offset of a character inside a line, zero-based.
+ start: TextSize,
+ /// End offset of a character inside a line, zero-based.
+ end: TextSize,
+}
+
+impl WideChar {
+ /// Returns the length in 8-bit UTF-8 code units.
+ fn len(&self) -> TextSize {
+ self.end - self.start
+ }
+
+ /// Returns the length in UTF-16 or UTF-32 code units.
+ fn wide_len(&self, enc: WideEncoding) -> u32 {
+ match enc {
+ WideEncoding::Utf16 => {
+ if self.len() == TextSize::from(4) {
+ 2
+ } else {
+ 1
+ }
+ }
+ WideEncoding::Utf32 => 1,
+ }
+ }
+}
+
+/// Maps flat [`TextSize`] offsets to/from `(line, column)` representation.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LineIndex {
+ /// Offset the beginning of each line (except the first, which always has offset 0).
+ newlines: Box<[TextSize]>,
+ /// List of non-ASCII characters on each line.
+ line_wide_chars: IntMap<u32, Box<[WideChar]>>,
+ /// The length of the entire text.
+ len: TextSize,
+}
+
+impl LineIndex {
+ /// Returns a `LineIndex` for the `text`.
+ pub fn new(text: &str) -> LineIndex {
+ let mut newlines = Vec::<TextSize>::with_capacity(16);
+ let mut line_wide_chars = IntMap::<u32, Box<[WideChar]>>::default();
+
+ let mut wide_chars = Vec::<WideChar>::new();
+ let mut cur_row = TextSize::from(0);
+ let mut cur_col = TextSize::from(0);
+ let mut line = 0u32;
+
+ for c in text.chars() {
+ let c_len = TextSize::of(c);
+ cur_row += c_len;
+ if c == '\n' {
+ newlines.push(cur_row);
+
+ // Save any wide characters seen in the previous line
+ if !wide_chars.is_empty() {
+ let cs = std::mem::take(&mut wide_chars).into_boxed_slice();
+ line_wide_chars.insert(line, cs);
+ }
+
+ // Prepare for processing the next line
+ cur_col = TextSize::from(0);
+ line += 1;
+ continue;
+ }
+
+ if !c.is_ascii() {
+ wide_chars.push(WideChar { start: cur_col, end: cur_col + c_len });
+ }
+
+ cur_col += c_len;
+ }
+
+ // Save any wide characters seen in the last line
+ if !wide_chars.is_empty() {
+ line_wide_chars.insert(line, wide_chars.into_boxed_slice());
+ }
+
+ LineIndex {
+ newlines: newlines.into_boxed_slice(),
+ line_wide_chars,
+ len: TextSize::of(text),
+ }
+ }
+
+ /// Transforms the `TextSize` into a `LineCol`.
+ ///
+ /// # Panics
+ ///
+ /// If the offset is invalid. See [`Self::try_line_col`].
+ pub fn line_col(&self, offset: TextSize) -> LineCol {
+ self.try_line_col(offset).expect("invalid offset")
+ }
+
+ /// Transforms the `TextSize` into a `LineCol`.
+ ///
+ /// Returns `None` if the `offset` was invalid, e.g. if it extends past the end of the text or
+ /// points to the middle of a multi-byte character.
+ pub fn try_line_col(&self, offset: TextSize) -> Option<LineCol> {
+ if offset > self.len {
+ return None;
+ }
+ let line = self.newlines.partition_point(|&it| it <= offset);
+ let start = self.start_offset(line)?;
+ let col = offset - start;
+ let ret = LineCol { line: line as u32, col: col.into() };
+ self.line_wide_chars
+ .get(&ret.line)
+ .into_iter()
+ .flat_map(|it| it.iter())
+ .all(|it| col <= it.start || it.end <= col)
+ .then_some(ret)
+ }
+
+ /// Transforms the `LineCol` into a `TextSize`.
+ pub fn offset(&self, line_col: LineCol) -> Option<TextSize> {
+ self.start_offset(line_col.line as usize).map(|start| start + TextSize::from(line_col.col))
+ }
+
+ fn start_offset(&self, line: usize) -> Option<TextSize> {
+ match line.checked_sub(1) {
+ None => Some(TextSize::from(0)),
+ Some(it) => self.newlines.get(it).copied(),
+ }
+ }
+
+ /// Transforms the `LineCol` with the given `WideEncoding` into a `WideLineCol`.
+ pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> Option<WideLineCol> {
+ let mut col = line_col.col;
+ if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) {
+ for c in wide_chars.iter() {
+ if u32::from(c.end) <= line_col.col {
+ col = col.checked_sub(u32::from(c.len()) - c.wide_len(enc))?;
+ } else {
+ // From here on, all utf16 characters come *after* the character we are mapping,
+ // so we don't need to take them into account
+ break;
+ }
+ }
+ }
+ Some(WideLineCol { line: line_col.line, col })
+ }
+
+ /// Transforms the `WideLineCol` with the given `WideEncoding` into a `LineCol`.
+ pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> Option<LineCol> {
+ let mut col = line_col.col;
+ if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) {
+ for c in wide_chars.iter() {
+ if col > u32::from(c.start) {
+ col = col.checked_add(u32::from(c.len()) - c.wide_len(enc))?;
+ } else {
+ // From here on, all utf16 characters come *after* the character we are mapping,
+ // so we don't need to take them into account
+ break;
+ }
+ }
+ }
+ Some(LineCol { line: line_col.line, col })
+ }
+
+ /// Given a range [start, end), returns a sorted iterator of non-empty ranges [start, x1), [x1,
+ /// x2), ..., [xn, end) where all the xi, which are positions of newlines, are inside the range
+ /// [start, end).
+ pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
+ let lo = self.newlines.partition_point(|&it| it < range.start());
+ let hi = self.newlines.partition_point(|&it| it <= range.end());
+ let all = std::iter::once(range.start())
+ .chain(self.newlines[lo..hi].iter().copied())
+ .chain(std::iter::once(range.end()));
+
+ all.clone()
+ .zip(all.skip(1))
+ .map(|(lo, hi)| TextRange::new(lo, hi))
+ .filter(|it| !it.is_empty())
+ }
+
+ /// Returns the length of the original text.
+ pub fn len(&self) -> TextSize {
+ self.len
+ }
+}
diff --git a/src/tools/rust-analyzer/lib/line-index/src/tests.rs b/src/tools/rust-analyzer/lib/line-index/src/tests.rs
new file mode 100644
index 000000000..31c01c20e
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/src/tests.rs
@@ -0,0 +1,11 @@
+use super::LineIndex;
+
+#[test]
+fn test_empty_index() {
+ let col_index = LineIndex::new(
+ "
+const C: char = 'x';
+",
+ );
+ assert_eq!(col_index.line_wide_chars.len(), 0);
+}
diff --git a/src/tools/rust-analyzer/lib/line-index/tests/it.rs b/src/tools/rust-analyzer/lib/line-index/tests/it.rs
new file mode 100644
index 000000000..ce1c0bc6f
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/tests/it.rs
@@ -0,0 +1,62 @@
+use line_index::{LineCol, LineIndex, TextRange};
+
+#[test]
+fn test_line_index() {
+ let text = "hello\nworld";
+ let table = [
+ (00, 0, 0),
+ (01, 0, 1),
+ (05, 0, 5),
+ (06, 1, 0),
+ (07, 1, 1),
+ (08, 1, 2),
+ (10, 1, 4),
+ (11, 1, 5),
+ ];
+
+ let index = LineIndex::new(text);
+ for (offset, line, col) in table {
+ assert_eq!(index.line_col(offset.into()), LineCol { line, col });
+ }
+
+ let text = "\nhello\nworld";
+ let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
+ let index = LineIndex::new(text);
+ for (offset, line, col) in table {
+ assert_eq!(index.line_col(offset.into()), LineCol { line, col });
+ }
+}
+
+#[test]
+fn test_char_len() {
+ assert_eq!('メ'.len_utf8(), 3);
+ assert_eq!('メ'.len_utf16(), 1);
+}
+
+#[test]
+fn test_splitlines() {
+ fn r(lo: u32, hi: u32) -> TextRange {
+ TextRange::new(lo.into(), hi.into())
+ }
+
+ let text = "a\nbb\nccc\n";
+ let line_index = LineIndex::new(text);
+
+ let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
+ let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
+ assert_eq!(actual, expected);
+
+ let text = "";
+ let line_index = LineIndex::new(text);
+
+ let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
+ let expected = vec![];
+ assert_eq!(actual, expected);
+
+ let text = "\n";
+ let line_index = LineIndex::new(text);
+
+ let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
+ let expected = vec![r(0, 1)];
+ assert_eq!(actual, expected)
+}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index 6e32e3960..e78a9d2eb 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -8,8 +8,8 @@ edition = "2021"
[dependencies]
log = "0.4.17"
-serde_json = "1.0.86"
-serde = { version = "1.0.144", features = ["derive"] }
+serde_json.workspace = true
+serde.workspace = true
crossbeam-channel = "0.5.6"
[dev-dependencies]
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
index 4c934d9ec..755b3fd95 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
@@ -2,7 +2,7 @@ use std::fmt;
use crate::{Notification, Request};
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub struct ProtocolError(pub(crate) String);
impl std::error::Error for ProtocolError {}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
index beccde40a..affab60a2 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
@@ -126,6 +126,9 @@ impl Connection {
self.sender.send(resp.into()).unwrap();
continue;
}
+ Ok(Message::Notification(n)) if !n.is_exit() => {
+ continue;
+ }
Ok(msg) => Err(ProtocolError(format!("expected initialize request, got {msg:?}"))),
Err(e) => {
Err(ProtocolError(format!("expected initialize request, got error: {e}")))
@@ -212,3 +215,70 @@ impl Connection {
Ok(true)
}
}
+
+#[cfg(test)]
+mod tests {
+ use crossbeam_channel::unbounded;
+ use lsp_types::notification::{Exit, Initialized, Notification};
+ use lsp_types::request::{Initialize, Request};
+ use lsp_types::{InitializeParams, InitializedParams};
+ use serde_json::to_value;
+
+ use crate::{Connection, Message, ProtocolError, RequestId};
+
+ struct TestCase {
+ test_messages: Vec<Message>,
+ expected_resp: Result<(RequestId, serde_json::Value), ProtocolError>,
+ }
+
+ fn initialize_start_test(test_case: TestCase) {
+ let (reader_sender, reader_receiver) = unbounded::<Message>();
+ let (writer_sender, writer_receiver) = unbounded::<Message>();
+ let conn = Connection { sender: writer_sender, receiver: reader_receiver };
+
+ for msg in test_case.test_messages {
+ assert!(reader_sender.send(msg).is_ok());
+ }
+
+ let resp = conn.initialize_start();
+ assert_eq!(test_case.expected_resp, resp);
+
+ assert!(writer_receiver.recv_timeout(std::time::Duration::from_secs(1)).is_err());
+ }
+
+ #[test]
+ fn not_exit_notification() {
+ let notification = crate::Notification {
+ method: Initialized::METHOD.to_string(),
+ params: to_value(InitializedParams {}).unwrap(),
+ };
+
+ let params_as_value = to_value(InitializeParams::default()).unwrap();
+ let req_id = RequestId::from(234);
+ let request = crate::Request {
+ id: req_id.clone(),
+ method: Initialize::METHOD.to_string(),
+ params: params_as_value.clone(),
+ };
+
+ initialize_start_test(TestCase {
+ test_messages: vec![notification.into(), request.into()],
+ expected_resp: Ok((req_id, params_as_value)),
+ });
+ }
+
+ #[test]
+ fn exit_notification() {
+ let notification =
+ crate::Notification { method: Exit::METHOD.to_string(), params: to_value(()).unwrap() };
+ let notification_msg = Message::from(notification);
+
+ initialize_start_test(TestCase {
+ test_messages: vec![notification_msg.clone()],
+ expected_resp: Err(ProtocolError(format!(
+ "expected initialize request, got {:?}",
+ notification_msg
+ ))),
+ });
+ }
+}
diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml
index 97734f048..471f2b5ac 100644
--- a/src/tools/rust-installer/Cargo.toml
+++ b/src/tools/rust-installer/Cargo.toml
@@ -2,7 +2,7 @@
authors = ["The Rust Project Developers"]
name = "installer"
version = "0.0.0"
-edition = "2018"
+edition = "2021"
[[bin]]
doc = false
@@ -17,8 +17,7 @@ tar = "0.4.38"
walkdir = "2"
xz2 = "0.1.4"
num_cpus = "1"
-remove_dir_all = "0.5"
[dependencies.clap]
features = ["derive"]
-version = "3.1"
+version = "4.2"
diff --git a/src/tools/rust-installer/combine-installers.sh b/src/tools/rust-installer/combine-installers.sh
index bee5319fd..01e5a00af 100755
--- a/src/tools/rust-installer/combine-installers.sh
+++ b/src/tools/rust-installer/combine-installers.sh
@@ -11,5 +11,9 @@ abs_path() {
(unset CDPATH && cd "$path" > /dev/null && pwd)
}
+# Running cargo will read the libstd Cargo.toml
+# which uses the unstable `public-dependency` feature.
+export RUSTC_BOOTSTRAP=1
+
src_dir="$(abs_path $(dirname "$0"))"
$CARGO run --manifest-path="$src_dir/Cargo.toml" -- combine "$@"
diff --git a/src/tools/rust-installer/gen-installer.sh b/src/tools/rust-installer/gen-installer.sh
index eabd8c95c..cc45b5e08 100755
--- a/src/tools/rust-installer/gen-installer.sh
+++ b/src/tools/rust-installer/gen-installer.sh
@@ -11,5 +11,9 @@ abs_path() {
(unset CDPATH && cd "$path" > /dev/null && pwd)
}
+# Running cargo will read the libstd Cargo.toml
+# which uses the unstable `public-dependency` feature.
+export RUSTC_BOOTSTRAP=1
+
src_dir="$(abs_path $(dirname "$0"))"
$CARGO run --manifest-path="$src_dir/Cargo.toml" -- generate "$@"
diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh
index 92a3f1f2c..6415644e0 100644
--- a/src/tools/rust-installer/install-template.sh
+++ b/src/tools/rust-installer/install-template.sh
@@ -17,8 +17,8 @@ log_line() {
local _line="$1"
if [ -n "${LOGFILE-}" -a -e "${LOGFILE-}" ]; then
- echo "$_line" >> "$LOGFILE"
- # Ignore errors, which may happen e.g. after the manifest dir is deleted
+ echo "$_line" >> "$LOGFILE"
+ # Ignore errors, which may happen e.g. after the manifest dir is deleted
fi
}
@@ -30,9 +30,9 @@ msg() {
verbose_msg() {
if [ -n "${CFG_VERBOSE-}" ]; then
- msg "${1-}"
+ msg "${1-}"
else
- log_line "install: ${1-}"
+ log_line "install: ${1-}"
fi
}
@@ -44,13 +44,13 @@ step_msg() {
verbose_step_msg() {
if [ -n "${CFG_VERBOSE-}" ]; then
- msg
- msg "$1"
- msg
+ msg
+ msg "$1"
+ msg
else
- log_line ""
- log_line "install: $1"
- log_line ""
+ log_line ""
+ log_line "install: $1"
+ log_line ""
fi
}
@@ -91,7 +91,7 @@ critical_need_ok() {
want_ok() {
if [ $? -ne 0 ]; then
- warn "$1"
+ warn "$1"
fi
}
@@ -321,40 +321,40 @@ uninstall_legacy() {
# Uninstall from legacy manifests
local _md
for _md in $_legacy_manifest_dirs; do
- # First, uninstall from the installation prefix.
- # Errors are warnings - try to rm everything in the manifest even if some fail.
- if [ -f "$_abs_libdir/$_md/manifest" ]
- then
-
- # iterate through installed manifest and remove files
- local _p;
- while read _p; do
- # the installed manifest contains absolute paths
- msg "removing legacy file $_p"
- if [ -f "$_p" ]
- then
- run rm -f "$_p"
- want_ok "failed to remove $_p"
- else
- warn "supposedly installed file $_p does not exist!"
- fi
- done < "$_abs_libdir/$_md/manifest"
-
- # If we fail to remove $md below, then the
- # installed manifest will still be full; the installed manifest
- # needs to be empty before install.
- msg "removing legacy manifest $_abs_libdir/$_md/manifest"
- run rm -f "$_abs_libdir/$_md/manifest"
- # For the above reason, this is a hard error
- need_ok "failed to remove installed manifest"
-
- # Remove $template_rel_manifest_dir directory
- msg "removing legacy manifest dir $_abs_libdir/$_md"
- run rm -R "$_abs_libdir/$_md"
- want_ok "failed to remove $_md"
-
- _uninstalled_something=true
- fi
+ # First, uninstall from the installation prefix.
+ # Errors are warnings - try to rm everything in the manifest even if some fail.
+ if [ -f "$_abs_libdir/$_md/manifest" ]
+ then
+
+ # iterate through installed manifest and remove files
+ local _p;
+ while read _p; do
+ # the installed manifest contains absolute paths
+ msg "removing legacy file $_p"
+ if [ -f "$_p" ]
+ then
+ run rm -f "$_p"
+ want_ok "failed to remove $_p"
+ else
+ warn "supposedly installed file $_p does not exist!"
+ fi
+ done < "$_abs_libdir/$_md/manifest"
+
+ # If we fail to remove $md below, then the
+ # installed manifest will still be full; the installed manifest
+ # needs to be empty before install.
+ msg "removing legacy manifest $_abs_libdir/$_md/manifest"
+ run rm -f "$_abs_libdir/$_md/manifest"
+ # For the above reason, this is a hard error
+ need_ok "failed to remove installed manifest"
+
+ # Remove $template_rel_manifest_dir directory
+ msg "removing legacy manifest dir $_abs_libdir/$_md"
+ run rm -R "$_abs_libdir/$_md"
+ want_ok "failed to remove $_md"
+
+ _uninstalled_something=true
+ fi
done
RETVAL="$_uninstalled_something"
@@ -373,133 +373,134 @@ uninstall_components() {
uninstall_legacy "$_abs_libdir"
assert_nz "$RETVAL", "RETVAL"
if [ "$RETVAL" = true ]; then
- _uninstalled_something=true;
+ _uninstalled_something=true;
fi
# Load the version of the installed installer
local _installed_version=
if [ -f "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version" ]; then
- _installed_version=`cat "$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"`
+ _installed_version=`cat "$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"`
- # Sanity check
- if [ ! -n "$_installed_version" ]; then critical_err "rust installer version is empty"; fi
+ # Sanity check
+ if [ ! -n "$_installed_version" ]; then critical_err "rust installer version is empty"; fi
fi
# If there's something installed, then uninstall
if [ -n "$_installed_version" ]; then
- # Check the version of the installed installer
- case "$_installed_version" in
-
- # If this is a previous version, then upgrade in place to the
- # current version before uninstalling.
- 2 )
- # The only change between version 2 -> 3 is that components are placed
- # in subdirectories of the installer tarball. There are no changes
- # to the installed data format, so nothing to do.
- ;;
-
- # This is the current version. Nothing need to be done except uninstall.
- "$TEMPLATE_RUST_INSTALLER_VERSION")
- ;;
-
- # If this is an unknown (future) version then bail.
- * )
- echo "The copy of $TEMPLATE_PRODUCT_NAME at $_dest_prefix was installed using an"
- echo "unknown version ($_installed_version) of rust-installer."
- echo "Uninstall it first with the installer used for the original installation"
- echo "before continuing."
- exit 1
- ;;
- esac
-
- local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
- local _installed_components="$(cat "$_md/components")"
-
- # Uninstall (our components only) before reinstalling
- local _available_component
- for _available_component in $_components; do
- local _installed_component
- for _installed_component in $_installed_components; do
- if [ "$_available_component" = "$_installed_component" ]; then
- msg "uninstalling component '$_available_component'"
- local _component_manifest="$_md/manifest-$_installed_component"
-
- # Sanity check: there should be a component manifest
- if [ ! -f "$_component_manifest" ]; then
- critical_err "installed component '$_installed_component' has no manifest"
- fi
-
- # Iterate through installed component manifest and remove files
- local _directive
- while read _directive; do
-
- local _command=`echo $_directive | cut -f1 -d:`
- local _file=`echo $_directive | cut -f2 -d:`
-
- # Sanity checks
- if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
- if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
-
- case "$_command" in
- file)
- verbose_msg "removing file $_file"
- if [ -f "$_file" ]; then
- run rm -f "$_file"
- want_ok "failed to remove $_file"
- else
- warn "supposedly installed file $_file does not exist!"
- fi
- ;;
-
- dir)
- verbose_msg "removing directory $_file"
- run rm -r "$_file"
- want_ok "unable to remove directory $_file"
- ;;
-
- *)
- critical_err "unknown installation directive"
- ;;
- esac
-
- done < "$_component_manifest"
-
- # Remove the installed component manifest
- verbose_msg "removing component manifest $_component_manifest"
- run rm "$_component_manifest"
- # This is a hard error because the installation is unrecoverable
- critical_need_ok "failed to remove installed manifest for component '$_installed_component'"
-
- # Update the installed component list
- local _modified_components="$(sed "/^$_installed_component\$/d" "$_md/components")"
- write_to_file "$_modified_components" "$_md/components"
- critical_need_ok "failed to update installed component list"
- fi
- done
- done
-
- # If there are no remaining components delete the manifest directory,
- # but only if we're doing an uninstall - if we're doing an install,
- # then leave the manifest directory around to hang onto the logs,
- # and any files not managed by the installer.
- if [ -n "${CFG_UNINSTALL-}" ]; then
- local _remaining_components="$(cat "$_md/components")"
- if [ ! -n "$_remaining_components" ]; then
- verbose_msg "removing manifest directory $_md"
- run rm -r "$_md"
- want_ok "failed to remove $_md"
-
- maybe_unconfigure_ld
- fi
- fi
-
- _uninstalled_something=true
+ # Check the version of the installed installer
+ case "$_installed_version" in
+
+ # If this is a previous version, then upgrade in place to the
+ # current version before uninstalling.
+ 2 )
+ # The only change between version 2 -> 3 is that components are placed
+ # in subdirectories of the installer tarball. There are no changes
+ # to the installed data format, so nothing to do.
+ ;;
+
+ # This is the current version. Nothing need to be done except uninstall.
+ "$TEMPLATE_RUST_INSTALLER_VERSION")
+ ;;
+
+ # If this is an unknown (future) version then bail.
+ * )
+ echo "The copy of $TEMPLATE_PRODUCT_NAME at $_dest_prefix was installed using an"
+ echo "unknown version ($_installed_version) of rust-installer."
+ echo "Uninstall it first with the installer used for the original installation"
+ echo "before continuing."
+ exit 1
+ ;;
+ esac
+
+ local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+ local _installed_components="$(cat "$_md/components")"
+
+ # Uninstall (our components only) before reinstalling
+ local _available_component
+ for _available_component in $_components; do
+ local _installed_component
+ for _installed_component in $_installed_components; do
+ if [ "$_available_component" = "$_installed_component" ]; then
+ msg "uninstalling component '$_available_component'"
+ local _component_manifest="$_md/manifest-$_installed_component"
+
+ # Sanity check: there should be a component manifest
+ if [ ! -f "$_component_manifest" ]; then
+ critical_err "installed component '$_installed_component' has no manifest"
+ fi
+
+ # Iterate through installed component manifest and remove files
+ local _directive
+ while read _directive; do
+
+ local _command=`echo $_directive | cut -f1 -d:`
+ local _file=`echo $_directive | cut -f2 -d:`
+
+ # Sanity checks
+ if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
+ if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
+
+ case "$_command" in
+ file)
+ verbose_msg "removing file $_file"
+ if [ -f "$_file" ]; then
+ run rm -f "$_file"
+ want_ok "failed to remove $_file"
+ else
+ warn "supposedly installed file $_file does not exist!"
+ fi
+ ;;
+
+ dir)
+ verbose_msg "removing directory $_file"
+ run rm -r "$_file"
+ want_ok "unable to remove directory $_file"
+ ;;
+
+ *)
+ critical_err "unknown installation directive"
+ ;;
+ esac
+
+ done < "$_component_manifest"
+
+ # Remove the installed component manifest
+ verbose_msg "removing component manifest $_component_manifest"
+ run rm "$_component_manifest"
+ # This is a hard error because the installation is unrecoverable
+ local _err_cant_r_manifest="failed to remove installed manifest for component"
+ critical_need_ok "$_err_cant_r_manifest '$_installed_component'"
+
+ # Update the installed component list
+ local _modified_components="$(sed "/^$_installed_component\$/d" "$_md/components")"
+ write_to_file "$_modified_components" "$_md/components"
+ critical_need_ok "failed to update installed component list"
+ fi
+ done
+ done
+
+ # If there are no remaining components delete the manifest directory,
+ # but only if we're doing an uninstall - if we're doing an install,
+ # then leave the manifest directory around to hang onto the logs,
+ # and any files not managed by the installer.
+ if [ -n "${CFG_UNINSTALL-}" ]; then
+ local _remaining_components="$(cat "$_md/components")"
+ if [ ! -n "$_remaining_components" ]; then
+ verbose_msg "removing manifest directory $_md"
+ run rm -r "$_md"
+ want_ok "failed to remove $_md"
+
+ maybe_unconfigure_ld
+ fi
+ fi
+
+ _uninstalled_something=true
fi
# There's no installed version. If we were asked to uninstall, then that's a problem.
if [ -n "${CFG_UNINSTALL-}" -a "$_uninstalled_something" = false ]
then
- err "unable to find installation manifest at $CFG_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR"
+ err "unable to find installation manifest at $CFG_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR"
fi
}
@@ -512,73 +513,73 @@ install_components() {
local _component
for _component in $_components; do
- msg "installing component '$_component'"
+ msg "installing component '$_component'"
- # The file name of the manifest we're installing from
- local _input_manifest="$_src_dir/$_component/manifest.in"
+ # The file name of the manifest we're installing from
+ local _input_manifest="$_src_dir/$_component/manifest.in"
- # Sanity check: do we have our input manifests?
- if [ ! -f "$_input_manifest" ]; then
- critical_err "manifest for $_component does not exist at $_input_manifest"
- fi
+ # Sanity check: do we have our input manifests?
+ if [ ! -f "$_input_manifest" ]; then
+ critical_err "manifest for $_component does not exist at $_input_manifest"
+ fi
- # The installed manifest directory
- local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+ # The installed manifest directory
+ local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
- # The file name of the manifest we're going to create during install
- local _installed_manifest="$_md/manifest-$_component"
+ # The file name of the manifest we're going to create during install
+ local _installed_manifest="$_md/manifest-$_component"
- # Create the installed manifest, which we will fill in with absolute file paths
- touch "$_installed_manifest"
- critical_need_ok "failed to create installed manifest"
+ # Create the installed manifest, which we will fill in with absolute file paths
+ touch "$_installed_manifest"
+ critical_need_ok "failed to create installed manifest"
- # Add this component to the installed component list
- append_to_file "$_component" "$_md/components"
- critical_need_ok "failed to update components list for $_component"
+ # Add this component to the installed component list
+ append_to_file "$_component" "$_md/components"
+ critical_need_ok "failed to update components list for $_component"
- # Now install, iterate through the new manifest and copy files
- local _directive
- while read _directive; do
+ # Now install, iterate through the new manifest and copy files
+ local _directive
+ while read _directive; do
- local _command=`echo $_directive | cut -f1 -d:`
- local _file=`echo $_directive | cut -f2 -d:`
+ local _command=`echo $_directive | cut -f1 -d:`
+ local _file=`echo $_directive | cut -f2 -d:`
- # Sanity checks
- if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
- if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
+ # Sanity checks
+ if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
+ if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
- # Decide the destination of the file
- local _file_install_path="$_dest_prefix/$_file"
+ # Decide the destination of the file
+ local _file_install_path="$_dest_prefix/$_file"
- if echo "$_file" | grep "^etc/" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^etc\///')"
- _file_install_path="$CFG_SYSCONFDIR/$_f"
- fi
+ if echo "$_file" | grep "^etc/" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^etc\///')"
+ _file_install_path="$CFG_SYSCONFDIR/$_f"
+ fi
- if echo "$_file" | grep "^bin/" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^bin\///')"
- _file_install_path="$CFG_BINDIR/$_f"
- fi
+ if echo "$_file" | grep "^bin/" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^bin\///')"
+ _file_install_path="$CFG_BINDIR/$_f"
+ fi
- if echo "$_file" | grep "^lib/" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^lib\///')"
- _file_install_path="$CFG_LIBDIR/$_f"
- fi
+ if echo "$_file" | grep "^lib/" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^lib\///')"
+ _file_install_path="$CFG_LIBDIR/$_f"
+ fi
- if echo "$_file" | grep "^share" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^share\///')"
- _file_install_path="$CFG_DATADIR/$_f"
- fi
+ if echo "$_file" | grep "^share" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^share\///')"
+ _file_install_path="$CFG_DATADIR/$_f"
+ fi
- if echo "$_file" | grep "^share/man/" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^share\/man\///')"
- _file_install_path="$CFG_MANDIR/$_f"
- fi
+ if echo "$_file" | grep "^share/man/" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^share\/man\///')"
+ _file_install_path="$CFG_MANDIR/$_f"
+ fi
# HACK: Try to support overriding --docdir. Paths with the form
# "share/doc/$product/" can be redirected to a single --docdir
@@ -592,69 +593,69 @@ install_components() {
# this problem to be a big deal in practice.
if [ "$CFG_DOCDIR" != "<default>" ]
then
- if echo "$_file" | grep "^share/doc/" > /dev/null
- then
- local _f="$(echo "$_file" | sed 's/^share\/doc\/[^/]*\///')"
- _file_install_path="$CFG_DOCDIR/$_f"
- fi
+ if echo "$_file" | grep "^share/doc/" > /dev/null
+ then
+ local _f="$(echo "$_file" | sed 's/^share\/doc\/[^/]*\///')"
+ _file_install_path="$CFG_DOCDIR/$_f"
+ fi
fi
- # Make sure there's a directory for it
- make_dir_recursive "$(dirname "$_file_install_path")"
- critical_need_ok "directory creation failed"
+ # Make sure there's a directory for it
+ make_dir_recursive "$(dirname "$_file_install_path")"
+ critical_need_ok "directory creation failed"
- # Make the path absolute so we can uninstall it later without
- # starting from the installation cwd
- absolutify "$_file_install_path"
- _file_install_path="$RETVAL"
- assert_nz "$_file_install_path" "file_install_path"
+ # Make the path absolute so we can uninstall it later without
+ # starting from the installation cwd
+ absolutify "$_file_install_path"
+ _file_install_path="$RETVAL"
+ assert_nz "$_file_install_path" "file_install_path"
- case "$_command" in
- file )
+ case "$_command" in
+ file )
- verbose_msg "copying file $_file_install_path"
+ verbose_msg "copying file $_file_install_path"
- maybe_backup_path "$_file_install_path"
+ maybe_backup_path "$_file_install_path"
- if echo "$_file" | grep "^bin/" > /dev/null || test -x "$_src_dir/$_component/$_file"
- then
- run cp "$_src_dir/$_component/$_file" "$_file_install_path"
- run chmod 755 "$_file_install_path"
- else
- run cp "$_src_dir/$_component/$_file" "$_file_install_path"
- run chmod 644 "$_file_install_path"
- fi
- critical_need_ok "file creation failed"
+ if echo "$_file" | grep "^bin/" > /dev/null || test -x "$_src_dir/$_component/$_file"
+ then
+ run cp "$_src_dir/$_component/$_file" "$_file_install_path"
+ run chmod 755 "$_file_install_path"
+ else
+ run cp "$_src_dir/$_component/$_file" "$_file_install_path"
+ run chmod 644 "$_file_install_path"
+ fi
+ critical_need_ok "file creation failed"
- # Update the manifest
- append_to_file "file:$_file_install_path" "$_installed_manifest"
- critical_need_ok "failed to update manifest"
+ # Update the manifest
+ append_to_file "file:$_file_install_path" "$_installed_manifest"
+ critical_need_ok "failed to update manifest"
- ;;
+ ;;
- dir )
+ dir )
- verbose_msg "copying directory $_file_install_path"
+ verbose_msg "copying directory $_file_install_path"
- maybe_backup_path "$_file_install_path"
+ maybe_backup_path "$_file_install_path"
- run cp -R "$_src_dir/$_component/$_file" "$_file_install_path"
- critical_need_ok "failed to copy directory"
+ run cp -R "$_src_dir/$_component/$_file" "$_file_install_path"
+ critical_need_ok "failed to copy directory"
# Set permissions. 0755 for dirs, 644 for files
run chmod -R u+rwX,go+rX,go-w "$_file_install_path"
critical_need_ok "failed to set permissions on directory"
- # Update the manifest
- append_to_file "dir:$_file_install_path" "$_installed_manifest"
- critical_need_ok "failed to update manifest"
- ;;
+ # Update the manifest
+ append_to_file "dir:$_file_install_path" "$_installed_manifest"
+ critical_need_ok "failed to update manifest"
+ ;;
- *)
- critical_err "unknown installation directive"
- ;;
- esac
- done < "$_input_manifest"
+ *)
+ critical_err "unknown installation directive"
+ ;;
+ esac
+ done < "$_input_manifest"
done
}
@@ -667,33 +668,35 @@ maybe_configure_ld() {
if [ "$_ostype" = "Linux" -a ! -n "${CFG_DISABLE_LDCONFIG-}" ]; then
- # Fedora-based systems do not configure the dynamic linker to look
- # /usr/local/lib, which is our default installation directory. To
- # make things just work, try to put that directory in
- # /etc/ld.so.conf.d/rust-installer-v1 so ldconfig picks it up.
- # Issue #30.
- #
- # This will get rm'd when the last component is uninstalled in
- # maybe_unconfigure_ld.
- if [ "$_abs_libdir" = "/usr/local/lib" -a -d "/etc/ld.so.conf.d" ]; then
- echo "$_abs_libdir" > "/etc/ld.so.conf.d/rust-installer-v1-$TEMPLATE_REL_MANIFEST_DIR.conf"
- if [ $? -ne 0 ]; then
- # This shouldn't happen if we've gotten this far
- # installing to /usr/local
- warn "failed to update /etc/ld.so.conf.d. this is unexpected"
- fi
- fi
-
- verbose_msg "running ldconfig"
- if [ -n "${CFG_VERBOSE-}" ]; then
- ldconfig
- else
- ldconfig 2> /dev/null
- fi
- if [ $? -ne 0 ]
- then
- warn "failed to run ldconfig. this may happen when not installing as root. run with --verbose to see the error"
- fi
+ # Fedora-based systems do not configure the dynamic linker to look
+ # /usr/local/lib, which is our default installation directory. To
+ # make things just work, try to put that directory in
+ # /etc/ld.so.conf.d/rust-installer-v1 so ldconfig picks it up.
+ # Issue #30.
+ #
+ # This will get rm'd when the last component is uninstalled in
+ # maybe_unconfigure_ld.
+ if [ "$_abs_libdir" = "/usr/local/lib" -a -d "/etc/ld.so.conf.d" ]; then
+ echo "$_abs_libdir" > "/etc/ld.so.conf.d/rust-installer-v1-$TEMPLATE_REL_MANIFEST_DIR.conf"
+ if [ $? -ne 0 ]; then
+ # This shouldn't happen if we've gotten this far
+ # installing to /usr/local
+ warn "failed to update /etc/ld.so.conf.d. this is unexpected"
+ fi
+ fi
+
+ verbose_msg "running ldconfig"
+ if [ -n "${CFG_VERBOSE-}" ]; then
+ ldconfig
+ else
+ ldconfig 2> /dev/null
+ fi
+ if [ $? -ne 0 ]
+ then
+ local _warn_s="failed to run ldconfig. this may happen when \
+not installing as root. run with --verbose to see the error"
+ warn "$_warn_s"
+ fi
fi
}
@@ -702,7 +705,7 @@ maybe_unconfigure_ld() {
assert_nz "$_ostype" "ostype"
if [ "$_ostype" != "Linux" ]; then
- return 0
+ return 0
fi
rm "/etc/ld.so.conf.d/rust-installer-v1-$TEMPLATE_REL_MANIFEST_DIR.conf" 2> /dev/null
@@ -714,9 +717,9 @@ maybe_backup_path() {
local _file_install_path="$1"
if [ -e "$_file_install_path" ]; then
- msg "backing up existing file at $_file_install_path"
- run mv -f "$_file_install_path" "$_file_install_path.old"
- critical_need_ok "failed to back up $_file_install_path"
+ msg "backing up existing file at $_file_install_path"
+ run mv -f "$_file_install_path" "$_file_install_path.old"
+ critical_need_ok "failed to back up $_file_install_path"
fi
}
@@ -742,7 +745,7 @@ do_preflight_sanity_checks() {
touch "$CFG_LIBDIR/rust-install-probe" > /dev/null
if [ $? -ne 0 ]
then
- err "can't write to destination. consider \`sudo\`."
+ err "can't write to destination. consider \`sudo\`."
fi
rm "$CFG_LIBDIR/rust-install-probe"
need_ok "failed to remove install probe"
@@ -752,7 +755,7 @@ do_preflight_sanity_checks() {
verbose_msg "verifying destination is not the same as source"
local _prefix_dir="$(abs_path "$dest_prefix")"
if [ "$_src_dir" = "$_dest_prefix" -a "${CFG_UNINSTALL-}" != 1 ]; then
- err "cannot install to same directory as installer"
+ err "cannot install to same directory as installer"
fi
}
@@ -857,8 +860,8 @@ src_basename="$(basename "$0")"
# which just means 'uninstall my components'.
if [ "$src_basename" = "uninstall.sh" ]; then
if [ "${*:-}" != "" ]; then
- # Currently don't know what to do with arguments in this mode
- err "uninstall.sh does not take any arguments"
+ # Currently don't know what to do with arguments in this mode
+ err "uninstall.sh does not take any arguments"
fi
CFG_UNINSTALL=1
CFG_DESTDIR_PREFIX="$(abs_path "$src_dir/../../")"
@@ -885,7 +888,7 @@ if [ -n "${CFG_LIST_COMPONENTS-}" ]; then
echo "# Available components"
echo
for component in $components; do
- echo "* $component"
+ echo "* $component"
done
echo
exit 0
@@ -897,15 +900,15 @@ if [ -n "$CFG_COMPONENTS" ]; then
# Remove commas
user_components="$(echo "$CFG_COMPONENTS" | sed "s/,/ /g")"
for user_component in $user_components; do
- found=false
- for my_component in $components; do
- if [ "$user_component" = "$my_component" ]; then
- found=true
- fi
- done
- if [ "$found" = false ]; then
- err "unknown component: $user_component"
- fi
+ found=false
+ for my_component in $components; do
+ if [ "$user_component" = "$my_component" ]; then
+ found=true
+ fi
+ done
+ if [ "$found" = false ]; then
+ err "unknown component: $user_component"
+ fi
done
components="$user_components"
fi
@@ -937,9 +940,9 @@ fi
if [ -z "$components" ]; then
if [ -z "${CFG_UNINSTALL-}" ]; then
- err "no components selected for installation"
+ err "no components selected for installation"
else
- err "no components selected for uninstallation"
+ err "no components selected for uninstallation"
fi
fi
@@ -977,7 +980,9 @@ make_dir_recursive "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
need_ok "failed to create $TEMPLATE_REL_MANIFEST_DIR"
# Drop the version number into the manifest dir
-write_to_file "$TEMPLATE_RUST_INSTALLER_VERSION" "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"
+write_to_file "$TEMPLATE_RUST_INSTALLER_VERSION" \
+"$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"
+
critical_need_ok "failed to write installer version"
# Install the uninstaller
@@ -992,5 +997,3 @@ maybe_configure_ld "$abs_libdir"
echo
echo " $TEMPLATE_SUCCESS_MESSAGE"
echo
-
-
diff --git a/src/tools/rust-installer/make-tarballs.sh b/src/tools/rust-installer/make-tarballs.sh
index e342007da..374e103e8 100755
--- a/src/tools/rust-installer/make-tarballs.sh
+++ b/src/tools/rust-installer/make-tarballs.sh
@@ -11,5 +11,9 @@ abs_path() {
(unset CDPATH && cd "$path" > /dev/null && pwd)
}
+# Running cargo will read the libstd Cargo.toml
+# which uses the unstable `public-dependency` feature.
+export RUSTC_BOOTSTRAP=1
+
src_dir="$(abs_path $(dirname "$0"))"
$CARGO run --manifest-path="$src_dir/Cargo.toml" -- tarball "$@"
diff --git a/src/tools/rust-installer/src/combiner.rs b/src/tools/rust-installer/src/combiner.rs
index abcf59cfe..565d19d86 100644
--- a/src/tools/rust-installer/src/combiner.rs
+++ b/src/tools/rust-installer/src/combiner.rs
@@ -13,47 +13,47 @@ actor! {
#[derive(Debug)]
pub struct Combiner {
/// The name of the product, for display.
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
product_name: String = "Product",
/// The name of the package tarball.
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
package_name: String = "package",
/// The directory under lib/ where the manifest lives.
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
rel_manifest_dir: String = "packagelib",
/// The string to print after successful installation.
- #[clap(value_name = "MESSAGE")]
+ #[arg(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall.
- #[clap(value_name = "DIRS")]
+ #[arg(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// Installers to combine.
- #[clap(value_name = "FILE,FILE")]
+ #[arg(value_name = "FILE,FILE")]
input_tarballs: String = "",
/// Directory containing files that should not be installed.
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
non_installed_overlay: String = "",
/// The directory to do temporary work.
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
work_dir: String = "./workdir",
/// The location to put the final image and tarball.
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
output_dir: String = "./dist",
/// The profile used to compress the tarball.
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
}
}
@@ -71,30 +71,21 @@ impl Combiner {
// Merge each installer into the work directory of the new installer.
let components = create_new_file(package_dir.join("components"))?;
- for input_tarball in self
- .input_tarballs
- .split(',')
- .map(str::trim)
- .filter(|s| !s.is_empty())
+ for input_tarball in self.input_tarballs.split(',').map(str::trim).filter(|s| !s.is_empty())
{
// Extract the input tarballs
let compression =
CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
})?;
- Archive::new(compression.decode(input_tarball)?)
- .unpack(&self.work_dir)
- .with_context(|| {
- format!(
- "unable to extract '{}' into '{}'",
- &input_tarball, self.work_dir
- )
- })?;
+ Archive::new(compression.decode(input_tarball)?).unpack(&self.work_dir).with_context(
+ || format!("unable to extract '{}' into '{}'", &input_tarball, self.work_dir),
+ )?;
let pkg_name =
input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
let pkg_name = Path::new(pkg_name).file_name().unwrap();
- let pkg_dir = Path::new(&self.work_dir).join(&pkg_name);
+ let pkg_dir = Path::new(&self.work_dir).join(pkg_name);
// Verify the version number.
let mut version = String::new();
@@ -114,9 +105,9 @@ impl Combiner {
// All we need to do is copy the component directory. We could
// move it, but rustbuild wants to reuse the unpacked package
// dir for OS-specific installers on macOS and Windows.
- let component_dir = package_dir.join(&component);
+ let component_dir = package_dir.join(component);
create_dir(&component_dir)?;
- copy_recursive(&pkg_dir.join(&component), &component_dir)?;
+ copy_recursive(&pkg_dir.join(component), &component_dir)?;
// Merge the component name.
writeln!(&components, "{}", component).context("failed to write new components")?;
@@ -126,12 +117,8 @@ impl Combiner {
// Write the installer version.
let version = package_dir.join("rust-installer-version");
- writeln!(
- create_new_file(version)?,
- "{}",
- crate::RUST_INSTALLER_VERSION
- )
- .context("failed to write new installer version")?;
+ writeln!(create_new_file(version)?, "{}", crate::RUST_INSTALLER_VERSION)
+ .context("failed to write new installer version")?;
// Copy the overlay.
if !self.non_installed_overlay.is_empty() {
@@ -158,7 +145,7 @@ impl Combiner {
.input(self.package_name)
.output(path_to_str(&output)?.into())
.compression_profile(self.compression_profile)
- .compression_formats(self.compression_formats.clone());
+ .compression_formats(self.compression_formats);
tarballer.run()?;
Ok(())
diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs
index 7c9c946e0..902b2ec69 100644
--- a/src/tools/rust-installer/src/compression.rs
+++ b/src/tools/rust-installer/src/compression.rs
@@ -166,7 +166,7 @@ impl Default for CompressionFormats {
impl CompressionFormats {
pub(crate) fn iter(&self) -> impl Iterator<Item = CompressionFormat> + '_ {
- self.0.iter().map(|i| *i)
+ self.0.iter().copied()
}
}
diff --git a/src/tools/rust-installer/src/generator.rs b/src/tools/rust-installer/src/generator.rs
index ddd105259..b101e67d8 100644
--- a/src/tools/rust-installer/src/generator.rs
+++ b/src/tools/rust-installer/src/generator.rs
@@ -11,55 +11,55 @@ actor! {
#[derive(Debug)]
pub struct Generator {
/// The name of the product, for display
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
product_name: String = "Product",
/// The name of the component, distinct from other installed components
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
component_name: String = "component",
/// The name of the package, tarball
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
package_name: String = "package",
/// The directory under lib/ where the manifest lives
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
rel_manifest_dir: String = "packagelib",
/// The string to print after successful installation
- #[clap(value_name = "MESSAGE")]
+ #[arg(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall
- #[clap(value_name = "DIRS")]
+ #[arg(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// Directory containing files that should not be installed
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
non_installed_overlay: String = "",
/// Path prefixes of directories that should be installed/uninstalled in bulk
- #[clap(value_name = "DIRS")]
+ #[arg(value_name = "DIRS")]
bulk_dirs: String = "",
/// The directory containing the installation medium
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
image_dir: String = "./install_image",
/// The directory to do temporary work
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
work_dir: String = "./workdir",
/// The location to put the final image and tarball
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
output_dir: String = "./dist",
/// The profile used to compress the tarball.
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
}
}
@@ -86,12 +86,8 @@ impl Generator {
// Write the installer version (only used by combine-installers.sh)
let version = package_dir.join("rust-installer-version");
- writeln!(
- create_new_file(version)?,
- "{}",
- crate::RUST_INSTALLER_VERSION
- )
- .context("failed to write new installer version")?;
+ writeln!(create_new_file(version)?, "{}", crate::RUST_INSTALLER_VERSION)
+ .context("failed to write new installer version")?;
// Copy the overlay
if !self.non_installed_overlay.is_empty() {
@@ -118,7 +114,7 @@ impl Generator {
.input(self.package_name)
.output(path_to_str(&output)?.into())
.compression_profile(self.compression_profile)
- .compression_formats(self.compression_formats.clone());
+ .compression_formats(self.compression_formats);
tarballer.run()?;
Ok(())
@@ -128,33 +124,19 @@ impl Generator {
/// Copies the `src` directory recursively to `dst`, writing `manifest.in` too.
fn copy_and_manifest(src: &Path, dst: &Path, bulk_dirs: &str) -> Result<()> {
let mut manifest = create_new_file(dst.join("manifest.in"))?;
- let bulk_dirs: Vec<_> = bulk_dirs
- .split(',')
- .filter(|s| !s.is_empty())
- .map(Path::new)
- .collect();
+ let bulk_dirs: Vec<_> = bulk_dirs.split(',').filter(|s| !s.is_empty()).map(Path::new).collect();
let mut paths = BTreeSet::new();
copy_with_callback(src, dst, |path, file_type| {
// We need paths to be compatible with both Unix and Windows.
- if path
- .components()
- .filter_map(|c| c.as_os_str().to_str())
- .any(|s| s.contains('\\'))
- {
- bail!(
- "rust-installer doesn't support '\\' in path components: {:?}",
- path
- );
+ if path.components().filter_map(|c| c.as_os_str().to_str()).any(|s| s.contains('\\')) {
+ bail!("rust-installer doesn't support '\\' in path components: {:?}", path);
}
// Normalize to Unix-style path separators.
let normalized_string;
let mut string = path.to_str().ok_or_else(|| {
- format_err!(
- "rust-installer doesn't support non-Unicode paths: {:?}",
- path
- )
+ format_err!("rust-installer doesn't support non-Unicode paths: {:?}", path)
})?;
if string.contains('\\') {
normalized_string = string.replace('\\', "/");
diff --git a/src/tools/rust-installer/src/main.rs b/src/tools/rust-installer/src/main.rs
index be8a0d683..99acecdd4 100644
--- a/src/tools/rust-installer/src/main.rs
+++ b/src/tools/rust-installer/src/main.rs
@@ -19,8 +19,12 @@ fn main() -> Result<()> {
let command_line = CommandLine::parse();
match command_line.command {
Subcommand::Combine(combiner) => combiner.run().context("failed to combine installers")?,
- Subcommand::Generate(generator) => generator.run().context("failed to generate installer")?,
- Subcommand::Script(scripter) => scripter.run().context("failed to generate installation script")?,
+ Subcommand::Generate(generator) => {
+ generator.run().context("failed to generate installer")?
+ }
+ Subcommand::Script(scripter) => {
+ scripter.run().context("failed to generate installation script")?
+ }
Subcommand::Tarball(tarballer) => tarballer.run().context("failed to generate tarballs")?,
}
Ok(())
diff --git a/src/tools/rust-installer/src/scripter.rs b/src/tools/rust-installer/src/scripter.rs
index 06affc029..ff65e818b 100644
--- a/src/tools/rust-installer/src/scripter.rs
+++ b/src/tools/rust-installer/src/scripter.rs
@@ -2,29 +2,29 @@ use crate::util::*;
use anyhow::{Context, Result};
use std::io::Write;
-const TEMPLATE: &'static str = include_str!("../install-template.sh");
+const TEMPLATE: &str = include_str!("../install-template.sh");
actor! {
#[derive(Debug)]
pub struct Scripter {
/// The name of the product, for display
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
product_name: String = "Product",
/// The directory under lib/ where the manifest lives
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
rel_manifest_dir: String = "manifestlib",
/// The string to print after successful installation
- #[clap(value_name = "MESSAGE")]
+ #[arg(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall
- #[clap(value_name = "DIRS")]
+ #[arg(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// The name of the output script
- #[clap(value_name = "FILE")]
+ #[arg(value_name = "FILE")]
output_script: String = "install.sh",
}
}
@@ -32,22 +32,19 @@ actor! {
impl Scripter {
/// Generates the actual installer script
pub fn run(self) -> Result<()> {
- // Replace dashes in the success message with spaces (our arg handling botches spaces)
- // TODO: still needed? Kept for compatibility for now.
+ // Replace dashes in the product name with spaces (our arg handling botches spaces)
+ // FIXME: still needed? Kept for compatibility for now.
let product_name = self.product_name.replace('-', " ");
// Replace dashes in the success message with spaces (our arg handling botches spaces)
- // TODO: still needed? Kept for compatibility for now.
+ // FIXME: still needed? Kept for compatibility for now.
let success_message = self.success_message.replace('-', " ");
let script = TEMPLATE
.replace("%%TEMPLATE_PRODUCT_NAME%%", &sh_quote(&product_name))
.replace("%%TEMPLATE_REL_MANIFEST_DIR%%", &self.rel_manifest_dir)
.replace("%%TEMPLATE_SUCCESS_MESSAGE%%", &sh_quote(&success_message))
- .replace(
- "%%TEMPLATE_LEGACY_MANIFEST_DIRS%%",
- &sh_quote(&self.legacy_manifest_dirs),
- )
+ .replace("%%TEMPLATE_LEGACY_MANIFEST_DIRS%%", &sh_quote(&self.legacy_manifest_dirs))
.replace(
"%%TEMPLATE_RUST_INSTALLER_VERSION%%",
&sh_quote(&crate::RUST_INSTALLER_VERSION),
diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs
index 7353a49fe..7572dc6dc 100644
--- a/src/tools/rust-installer/src/tarballer.rs
+++ b/src/tools/rust-installer/src/tarballer.rs
@@ -14,23 +14,23 @@ actor! {
#[derive(Debug)]
pub struct Tarballer {
/// The input folder to be compressed.
- #[clap(value_name = "NAME")]
+ #[arg(value_name = "NAME")]
input: String = "package",
/// The prefix of the tarballs.
- #[clap(value_name = "PATH")]
+ #[arg(value_name = "PATH")]
output: String = "./dist",
/// The folder in which the input is to be found.
- #[clap(value_name = "DIR")]
+ #[arg(value_name = "DIR")]
work_dir: String = "./workdir",
/// The profile used to compress the tarball.
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball.
- #[clap(value_name = "FORMAT", default_value_t)]
+ #[arg(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
}
}
@@ -58,10 +58,7 @@ impl Tarballer {
let buf = BufWriter::with_capacity(1024 * 1024, encoder);
let mut builder = Builder::new(buf);
- let pool = rayon::ThreadPoolBuilder::new()
- .num_threads(2)
- .build()
- .unwrap();
+ let pool = rayon::ThreadPoolBuilder::new().num_threads(2).build().unwrap();
pool.install(move || {
for path in dirs {
let src = Path::new(&self.work_dir).join(&path);
@@ -98,7 +95,7 @@ fn append_path<W: Write>(builder: &mut Builder<W>, src: &Path, path: &String) ->
if cfg!(windows) {
// Windows doesn't really have a mode, so `tar` never marks files executable.
// Use an extension whitelist to update files that usually should be so.
- const EXECUTABLES: [&'static str; 4] = ["exe", "dll", "py", "sh"];
+ const EXECUTABLES: [&str; 4] = ["exe", "dll", "py", "sh"];
if let Some(ext) = src.extension().and_then(|s| s.to_str()) {
if EXECUTABLES.contains(&ext) {
let mode = header.mode()?;
@@ -122,11 +119,7 @@ where
let name = name.as_ref();
if !name.is_relative() && !name.starts_with(root) {
- bail!(
- "input '{}' is not in work dir '{}'",
- name.display(),
- root.display()
- );
+ bail!("input '{}' is not in work dir '{}'", name.display(), root.display());
}
let mut dirs = vec![];
@@ -134,7 +127,7 @@ where
for entry in WalkDir::new(root.join(name)) {
let entry = entry?;
let path = entry.path().strip_prefix(root)?;
- let path = path_to_str(&path)?;
+ let path = path_to_str(path)?;
if entry.file_type().is_dir() {
dirs.push(path.to_owned());
diff --git a/src/tools/rust-installer/src/util.rs b/src/tools/rust-installer/src/util.rs
index 674617c65..47ca0cfa3 100644
--- a/src/tools/rust-installer/src/util.rs
+++ b/src/tools/rust-installer/src/util.rs
@@ -15,8 +15,7 @@ use std::os::windows::fs::symlink_file;
/// Converts a `&Path` to a UTF-8 `&str`.
pub fn path_to_str(path: &Path) -> Result<&str> {
- path.to_str()
- .ok_or_else(|| format_err!("path is not valid UTF-8 '{}'", path.display()))
+ path.to_str().ok_or_else(|| format_err!("path is not valid UTF-8 '{}'", path.display()))
}
/// Wraps `fs::copy` with a nicer error message.
@@ -27,11 +26,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
Ok(0)
} else {
let amt = fs::copy(&from, &to).with_context(|| {
- format!(
- "failed to copy '{}' to '{}'",
- from.as_ref().display(),
- to.as_ref().display()
- )
+ format!("failed to copy '{}' to '{}'", from.as_ref().display(), to.as_ref().display())
})?;
Ok(amt)
}
@@ -82,7 +77,7 @@ pub fn open_file<P: AsRef<Path>>(path: P) -> Result<fs::File> {
/// Wraps `remove_dir_all` with a nicer error message.
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
- remove_dir_all::remove_dir_all(path.as_ref())
+ fs::remove_dir_all(path.as_ref())
.with_context(|| format!("failed to remove dir '{}'", path.as_ref().display()))?;
Ok(())
}
@@ -117,14 +112,18 @@ where
} else {
copy(entry.path(), dst)?;
}
- callback(&path, file_type)?;
+ callback(path, file_type)?;
}
Ok(())
}
macro_rules! actor_field_default {
- () => { Default::default() };
- (= $expr:expr) => { $expr.into() }
+ () => {
+ Default::default()
+ };
+ (= $expr:expr) => {
+ $expr.into()
+ };
}
/// Creates an "actor" with default values, setters for all fields, and Clap parser support.
@@ -135,7 +134,7 @@ macro_rules! actor {
$( #[ $attr ] )+
#[derive(clap::Args)]
pub struct $name {
- $( $( #[ $field_attr ] )+ #[clap(long, $(default_value = $default)*)] $field : $type, )*
+ $( $( #[ $field_attr ] )+ #[arg(long, $(default_value = $default)*)] $field : $type, )*
}
impl Default for $name {
diff --git a/src/tools/rust-installer/test.sh b/src/tools/rust-installer/test.sh
index dac6f77ef..4f69bfc63 100755
--- a/src/tools/rust-installer/test.sh
+++ b/src/tools/rust-installer/test.sh
@@ -27,7 +27,7 @@ PREFIX_DIR="$TMP_DIR/prefix"
case $(uname -s) in
MINGW* | MSYS*)
- WINDOWS=1
+ WINDOWS=1
;;
esac
@@ -48,10 +48,10 @@ pre() {
need_ok() {
if [ $? -ne 0 ]
then
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
fi
}
@@ -69,20 +69,20 @@ try() {
_cmd="$@"
_output=`$@ 2>&1`
if [ $? -ne 0 ]; then
- echo \$ "$_cmd"
- # Using /bin/echo to avoid escaping
- $ECHO "$_output"
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ # Using /bin/echo to avoid escaping
+ $ECHO "$_output"
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
else
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
- echo \$ "$_cmd"
- fi
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
- $ECHO "$_output"
- fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+ echo \$ "$_cmd"
+ fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+ $ECHO "$_output"
+ fi
fi
set -e
}
@@ -92,20 +92,20 @@ expect_fail() {
_cmd="$@"
_output=`$@ 2>&1`
if [ $? -eq 0 ]; then
- echo \$ "$_cmd"
- # Using /bin/echo to avoid escaping
- $ECHO "$_output"
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ # Using /bin/echo to avoid escaping
+ $ECHO "$_output"
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
else
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
- echo \$ "$_cmd"
- fi
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
- $ECHO "$_output"
- fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+ echo \$ "$_cmd"
+ fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+ $ECHO "$_output"
+ fi
fi
set -e
}
@@ -117,30 +117,30 @@ expect_output_ok() {
_cmd="$@"
_output=`$@ 2>&1`
if [ $? -ne 0 ]; then
- echo \$ "$_cmd"
- # Using /bin/echo to avoid escaping
- $ECHO "$_output"
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ # Using /bin/echo to avoid escaping
+ $ECHO "$_output"
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
elif ! echo "$_output" | grep -q "$_expected"; then
- echo \$ "$_cmd"
- $ECHO "$_output"
- echo
- echo "missing expected output '$_expected'"
- echo
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ $ECHO "$_output"
+ echo
+ echo "missing expected output '$_expected'"
+ echo
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
else
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
- echo \$ "$_cmd"
- fi
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
- $ECHO "$_output"
- fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+ echo \$ "$_cmd"
+ fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+ $ECHO "$_output"
+ fi
fi
set -e
}
@@ -152,30 +152,30 @@ expect_output_fail() {
_cmd="$@"
_output=`$@ 2>&1`
if [ $? -eq 0 ]; then
- echo \$ "$_cmd"
- # Using /bin/echo to avoid escaping
- $ECHO "$_output"
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ # Using /bin/echo to avoid escaping
+ $ECHO "$_output"
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
elif ! echo "$_output" | grep -q "$_expected"; then
- echo \$ "$_cmd"
- $ECHO "$_output"
- echo
- echo "missing expected output '$_expected'"
- echo
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ $ECHO "$_output"
+ echo
+ echo "missing expected output '$_expected'"
+ echo
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
else
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
- echo \$ "$_cmd"
- fi
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
- $ECHO "$_output"
- fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+ echo \$ "$_cmd"
+ fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+ $ECHO "$_output"
+ fi
fi
set -e
}
@@ -187,30 +187,30 @@ expect_not_output_ok() {
_cmd="$@"
_output=`$@ 2>&1`
if [ $? -ne 0 ]; then
- echo \$ "$_cmd"
- # Using /bin/echo to avoid escaping
- $ECHO "$_output"
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ # Using /bin/echo to avoid escaping
+ $ECHO "$_output"
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
elif echo "$_output" | grep -q "$_expected"; then
- echo \$ "$_cmd"
- $ECHO "$_output"
- echo
- echo "unexpected output '$_expected'"
- echo
- echo
- echo "TEST FAILED!"
- echo
- exit 1
+ echo \$ "$_cmd"
+ $ECHO "$_output"
+ echo
+ echo "unexpected output '$_expected'"
+ echo
+ echo
+ echo "TEST FAILED!"
+ echo
+ exit 1
else
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
- echo \$ "$_cmd"
- fi
- if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
- $ECHO "$_output"
- fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+ echo \$ "$_cmd"
+ fi
+ if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+ $ECHO "$_output"
+ fi
fi
set -e
}
@@ -218,9 +218,9 @@ expect_not_output_ok() {
runtest() {
local _testname="$1"
if [ -n "${TESTNAME-}" ]; then
- if ! echo "$_testname" | grep -q "$TESTNAME"; then
- return 0
- fi
+ if ! echo "$_testname" | grep -q "$TESTNAME"; then
+ return 0
+ fi
fi
pre "$_testname"
@@ -231,9 +231,9 @@ runtest() {
basic_install() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
try test -e "$PREFIX_DIR/dir-to-install/foo"
@@ -245,9 +245,9 @@ runtest basic_install
basic_uninstall() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try "$WORK_DIR/package/install.sh --uninstall" --prefix="$PREFIX_DIR"
try test ! -e "$PREFIX_DIR/something-to-install"
@@ -263,10 +263,10 @@ not_installed_files() {
mkdir -p "$WORK_DIR/overlay"
touch "$WORK_DIR/overlay/not-installed"
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --non-installed-overlay="$WORK_DIR/overlay"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --non-installed-overlay="$WORK_DIR/overlay"
try test -e "$WORK_DIR/package/not-installed"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test ! -e "$PREFIX_DIR/not-installed"
@@ -275,10 +275,10 @@ runtest not_installed_files
tarball_with_package_name() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc-nightly
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc-nightly
try "$WORK_DIR/rustc-nightly/install.sh" --prefix="$PREFIX_DIR"
try test -e "$OUT_DIR/rustc-nightly.tar.gz"
try test -e "$OUT_DIR/rustc-nightly.tar.xz"
@@ -287,9 +287,9 @@ runtest tarball_with_package_name
install_overwrite_backup() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try mkdir -p "$PREFIX_DIR/bin"
touch "$PREFIX_DIR/bin/program"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
@@ -300,10 +300,10 @@ runtest install_overwrite_backup
bulk_directory() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --bulk-dirs=dir-to-install
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --bulk-dirs=dir-to-install
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
try test -e "$PREFIX_DIR/dir-to-install/foo"
@@ -317,10 +317,10 @@ runtest bulk_directory
bulk_directory_overwrite() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --bulk-dirs=dir-to-install
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --bulk-dirs=dir-to-install
try mkdir -p "$PREFIX_DIR/dir-to-install"
try touch "$PREFIX_DIR/dir-to-install/overwrite"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
@@ -333,10 +333,10 @@ runtest bulk_directory_overwrite
bulk_directory_overwrite_existing_backup() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --bulk-dirs=dir-to-install
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --bulk-dirs=dir-to-install
try mkdir -p "$PREFIX_DIR/dir-to-install"
try touch "$PREFIX_DIR/dir-to-install/overwrite"
# This time we've already got an existing backup of the overwritten directory.
@@ -351,10 +351,10 @@ runtest bulk_directory_overwrite_existing_backup
nested_bulk_directory() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --bulk-dirs=dir-to-install/qux
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --bulk-dirs=dir-to-install/qux
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/dir-to-install/qux/bar"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
@@ -364,10 +364,10 @@ runtest nested_bulk_directory
only_bulk_directory_no_files() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image5" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --bulk-dirs=dir-to-install
+ --image-dir="$TEST_DIR/image5" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --bulk-dirs=dir-to-install
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/dir-to-install/foo"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
@@ -379,10 +379,10 @@ nested_not_installed_files() {
mkdir -p "$WORK_DIR/overlay"
touch "$WORK_DIR/overlay/not-installed"
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --non-installed-overlay="$WORK_DIR/overlay"
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --non-installed-overlay="$WORK_DIR/overlay"
try test -e "$WORK_DIR/package/not-installed"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test ! -e "$PREFIX_DIR/not-installed"
@@ -391,15 +391,15 @@ runtest nested_not_installed_files
multiple_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR/c1" \
- --output-dir="$OUT_DIR/c1" \
- --component-name=rustc
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR/c1" \
+ --output-dir="$OUT_DIR/c1" \
+ --component-name=rustc
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR/c2" \
- --output-dir="$OUT_DIR/c2" \
- --component-name=cargo
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR/c2" \
+ --output-dir="$OUT_DIR/c2" \
+ --component-name=cargo
try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
@@ -422,15 +422,15 @@ runtest multiple_components
uninstall_from_installed_script() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR/c1" \
- --output-dir="$OUT_DIR/c1" \
- --component-name=rustc
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR/c1" \
+ --output-dir="$OUT_DIR/c1" \
+ --component-name=rustc
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR/c2" \
- --output-dir="$OUT_DIR/c2" \
- --component-name=cargo
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR/c2" \
+ --output-dir="$OUT_DIR/c2" \
+ --component-name=cargo
try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
@@ -453,12 +453,13 @@ runtest uninstall_from_installed_script
uninstall_from_installed_script_with_args_fails() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR/c1" \
- --output-dir="$OUT_DIR/c1" \
- --component-name=rustc
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR/c1" \
+ --output-dir="$OUT_DIR/c1" \
+ --component-name=rustc
try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
- expect_output_fail "uninstall.sh does not take any arguments" sh "$PREFIX_DIR/lib/packagelib/uninstall.sh" --prefix=foo
+ expect_output_fail "uninstall.sh does not take any arguments" \
+ sh "$PREFIX_DIR/lib/packagelib/uninstall.sh" --prefix=foo
}
runtest uninstall_from_installed_script_with_args_fails
@@ -466,22 +467,22 @@ runtest uninstall_from_installed_script_with_args_fails
combine_installers() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
try test -e "$PREFIX_DIR/dir-to-install/foo"
@@ -502,28 +503,28 @@ runtest combine_installers
combine_three_installers() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
try test -e "$PREFIX_DIR/dir-to-install/foo"
@@ -546,25 +547,25 @@ runtest combine_three_installers
combine_installers_with_overlay() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
mkdir -p "$WORK_DIR/overlay"
touch "$WORK_DIR/overlay/README"
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
- --non-installed-overlay="$WORK_DIR/overlay"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+ --non-installed-overlay="$WORK_DIR/overlay"
try test -e "$WORK_DIR/rust/README"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try test ! -e "$PREFIX_DIR/README"
@@ -573,23 +574,23 @@ runtest combine_installers_with_overlay
combined_with_bulk_dirs() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc \
- --bulk-dirs=dir-to-install
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc \
+ --bulk-dirs=dir-to-install
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/dir-to-install/foo"
try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
@@ -599,25 +600,25 @@ runtest combined_with_bulk_dirs
combine_install_with_separate_uninstall() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc \
- --rel-manifest-dir=rustlib
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo \
- --rel-manifest-dir=rustlib
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc \
+ --rel-manifest-dir=rustlib
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo \
+ --rel-manifest-dir=rustlib
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
- --rel-manifest-dir=rustlib
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+ --rel-manifest-dir=rustlib
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/something-to-install"
try test -e "$PREFIX_DIR/dir-to-install/foo"
@@ -639,28 +640,28 @@ runtest combine_install_with_separate_uninstall
select_components_to_install() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=rustc
try test -e "$PREFIX_DIR/bin/program"
try test ! -e "$PREFIX_DIR/bin/cargo"
@@ -680,7 +681,8 @@ select_components_to_install() {
try test -e "$PREFIX_DIR/bin/program"
try test -e "$PREFIX_DIR/bin/cargo"
try test ! -e "$PREFIX_DIR/baz"
- try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc,cargo,rust-docs
+ try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" \
+ --components=rustc,cargo,rust-docs
try test ! -e "$PREFIX_DIR/bin/program"
try test ! -e "$PREFIX_DIR/bin/cargo"
try test ! -e "$PREFIX_DIR/baz"
@@ -690,28 +692,28 @@ runtest select_components_to_install
select_components_to_uninstall() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc
try test ! -e "$PREFIX_DIR/bin/program"
@@ -733,7 +735,8 @@ select_components_to_uninstall() {
try test ! -e "$PREFIX_DIR/bin/cargo"
try test -e "$PREFIX_DIR/baz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
- try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc,cargo,rust-docs
+ try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" \
+ --components=rustc,cargo,rust-docs
try test ! -e "$PREFIX_DIR/bin/program"
try test ! -e "$PREFIX_DIR/bin/cargo"
try test ! -e "$PREFIX_DIR/baz"
@@ -743,56 +746,57 @@ runtest select_components_to_uninstall
invalid_component() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
- expect_output_fail "unknown component" "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=foo
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ expect_output_fail "unknown component" "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" \
+ --components=foo
}
runtest invalid_component
without_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs
try test -e "$PREFIX_DIR/bin/program"
try test -e "$PREFIX_DIR/bin/cargo"
@@ -815,28 +819,28 @@ runtest without_components
# --without causes components to remain installed
uninstall_without_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --without=rust-docs
try test ! -e "$PREFIX_DIR/bin/program"
@@ -857,88 +861,88 @@ runtest uninstall_without_components
without_any_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
expect_output_fail "no components selected for installation" \
- "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs,rustc,cargo
+ "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs,rustc,cargo
}
runtest without_any_components
uninstall_without_any_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
expect_output_fail "no components selected for uninstallation" \
- "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" \
- --uninstall --without=rust-docs,rustc,cargo
+ "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" \
+ --uninstall --without=rust-docs,rustc,cargo
}
runtest uninstall_without_any_components
list_components() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
expect_output_ok "rustc" "$WORK_DIR/rust/install.sh" --list-components
expect_output_ok "cargo" "$WORK_DIR/rust/install.sh" --list-components
expect_output_ok "rust-docs" "$WORK_DIR/rust/install.sh" --list-components
@@ -947,32 +951,32 @@ runtest list_components
combined_remains() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rustc \
- --component-name=rustc
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image3" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=cargo \
- --component-name=cargo
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image4" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust-docs \
- --component-name=rust-docs
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rustc \
+ --component-name=rustc
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image3" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=cargo \
+ --component-name=cargo
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image4" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust-docs \
+ --component-name=rust-docs
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
for component in rustc cargo rust-docs; do
- # rustbuild wants the original extracted package intact too
- try test -d "$WORK_DIR/$component/$component"
- try test -d "$WORK_DIR/rust/$component"
+ # rustbuild wants the original extracted package intact too
+ try test -d "$WORK_DIR/$component/$component"
+ try test -d "$WORK_DIR/rust/$component"
done
}
runtest combined_remains
@@ -982,34 +986,34 @@ runtest combined_remains
cannot_write_error() {
# chmod doesn't work on windows
if [ ! -n "${WINDOWS-}" ]; then
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
- chmod u-w "$PREFIX_DIR"
- expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
- chmod u+w "$PREFIX_DIR"
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
+ chmod u-w "$PREFIX_DIR"
+ expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+ chmod u+w "$PREFIX_DIR"
fi
}
runtest cannot_write_error
cannot_install_to_installer() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=my-package
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=my-package
expect_output_fail "cannot install to same directory as installer" \
- "$WORK_DIR/my-package/install.sh" --prefix="$WORK_DIR/my-package"
+ "$WORK_DIR/my-package/install.sh" --prefix="$WORK_DIR/my-package"
}
runtest cannot_install_to_installer
upgrade_from_future_installer_error() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --rel-manifest-dir=rustlib
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --rel-manifest-dir=rustlib
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
echo 100 > "$PREFIX_DIR/lib/rustlib/rust-installer-version"
expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
@@ -1018,9 +1022,9 @@ runtest upgrade_from_future_installer_error
destdir() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --destdir="$PREFIX_DIR/" --prefix=prefix
try test -e "$PREFIX_DIR/prefix/bin/program"
}
@@ -1028,9 +1032,9 @@ runtest destdir
destdir_no_trailing_slash() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --destdir="$PREFIX_DIR" --prefix=prefix
try test -e "$PREFIX_DIR/prefix/bin/program"
}
@@ -1055,7 +1059,7 @@ create_log() {
try test -e "$PREFIX_DIR/lib/packagelib/install.log"
local _log="$(cat "$PREFIX_DIR/lib/packagelib/install.log")"
if [ -z "$_log" ]; then
- fail "log is empty"
+ fail "log is empty"
fi
}
runtest create_log
@@ -1063,24 +1067,24 @@ runtest create_log
leave_log_after_failure() {
# chmod doesn't work on windows
if [ ! -n "${WINDOWS-}" ]; then
- try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
- mkdir -p "$PREFIX_DIR/lib/packagelib"
- touch "$PREFIX_DIR/lib/packagelib/components"
- chmod u-w "$PREFIX_DIR/lib/packagelib/components"
- expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
- chmod u+w "$PREFIX_DIR/lib/packagelib/components"
- try test -e "$PREFIX_DIR/lib/packagelib/install.log"
- local _log="$(cat "$PREFIX_DIR/lib/packagelib/install.log")"
- if [ -z "$_log" ]; then
- fail "log is empty"
- fi
- # script should tell user where the logs are
- if ! grep -q "see logs at" "$PREFIX_DIR/lib/packagelib/install.log"; then
- fail "missing log message"
- fi
+ try sh "$S/gen-installer.sh" \
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
+ mkdir -p "$PREFIX_DIR/lib/packagelib"
+ touch "$PREFIX_DIR/lib/packagelib/components"
+ chmod u-w "$PREFIX_DIR/lib/packagelib/components"
+ expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+ chmod u+w "$PREFIX_DIR/lib/packagelib/components"
+ try test -e "$PREFIX_DIR/lib/packagelib/install.log"
+ local _log="$(cat "$PREFIX_DIR/lib/packagelib/install.log")"
+ if [ -z "$_log" ]; then
+ fail "log is empty"
+ fi
+ # script should tell user where the logs are
+ if ! grep -q "see logs at" "$PREFIX_DIR/lib/packagelib/install.log"; then
+ fail "missing log message"
+ fi
fi
}
runtest leave_log_after_failure
@@ -1088,9 +1092,9 @@ runtest leave_log_after_failure
# https://github.com/rust-lang/rust-installer/issues/22
help() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --help
}
runtest help
@@ -1098,9 +1102,9 @@ runtest help
# https://github.com/rust-lang/rust-installer/issues/31
CDPATH_does_not_destroy_things() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
cd "$WORK_DIR" || exit 1
export CDPATH="../$(basename $WORK_DIR)/foo"
try sh "package/install.sh" --prefix="$PREFIX_DIR"
@@ -1115,9 +1119,9 @@ runtest CDPATH_does_not_destroy_things
docdir_default() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image-docdir1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image-docdir1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
try test -e "$PREFIX_DIR/share/doc/rust/README"
try test -e "$PREFIX_DIR/share/doc/rust/rustdocs.txt"
@@ -1126,9 +1130,9 @@ runtest docdir_default
docdir() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image-docdir1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR"
+ --image-dir="$TEST_DIR/image-docdir1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR"
try mkdir "$WORK_DIR/docdir"
try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --docdir="$WORK_DIR/docdir"
try test -e "$WORK_DIR/docdir/README"
@@ -1138,22 +1142,22 @@ runtest docdir
docdir_combined() {
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image-docdir1" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
+ --image-dir="$TEST_DIR/image-docdir1" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
--package-name="rustc" \
--component-name="rustc"
try sh "$S/gen-installer.sh" \
- --image-dir="$TEST_DIR/image-docdir2" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
+ --image-dir="$TEST_DIR/image-docdir2" \
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
--package-name="cargo" \
--component-name="cargo"
try sh "$S/combine-installers.sh" \
- --work-dir="$WORK_DIR" \
- --output-dir="$OUT_DIR" \
- --package-name=rust \
- --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+ --work-dir="$WORK_DIR" \
+ --output-dir="$OUT_DIR" \
+ --package-name=rust \
+ --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
try mkdir "$WORK_DIR/docdir"
try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --docdir="$WORK_DIR/docdir"
try test -e "$WORK_DIR/docdir/README"
diff --git a/src/tools/rustdoc-gui-test/Cargo.toml b/src/tools/rustdoc-gui-test/Cargo.toml
index f0c5b3671..4cb200ebc 100644
--- a/src/tools/rustdoc-gui-test/Cargo.toml
+++ b/src/tools/rustdoc-gui-test/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+build_helper = { path = "../build_helper" }
compiletest = { path = "../compiletest" }
getopts = "0.2"
walkdir = "2"
diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs
index 8dc18dfae..0ddd2c66c 100644
--- a/src/tools/rustdoc-gui-test/src/main.rs
+++ b/src/tools/rustdoc-gui-test/src/main.rs
@@ -1,3 +1,4 @@
+use build_helper::util::try_run;
use compiletest::header::TestProps;
use config::Config;
use std::path::{Path, PathBuf};
@@ -13,13 +14,19 @@ fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option<String>
if global {
command.arg("--global");
}
- let lines = command
- .output()
- .map(|output| String::from_utf8_lossy(&output.stdout).into_owned())
- .unwrap_or(String::new());
+ let lines = match command.output() {
+ Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(),
+ Err(e) => {
+ eprintln!(
+ "path to npm can be wrong, provided path: {npm:?}. Try to set npm path \
+ in config.toml in [build.npm]",
+ );
+ panic!("{:?}", e)
+ }
+ };
lines
.lines()
- .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
+ .find_map(|l| l.rsplit(':').next()?.strip_prefix("browser-ui-test@"))
.map(|v| v.to_owned())
}
@@ -60,24 +67,7 @@ fn find_librs<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
None
}
-// FIXME: move `bootstrap::util::try_run` into `build_helper` crate
-// and use that one instead of creating this function.
-fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
- let status = match cmd.status() {
- Ok(status) => status,
- Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cmd, e),
- };
- if !status.success() && print_cmd_on_fail {
- println!(
- "\n\ncommand did not execute successfully: {:?}\n\
- expected success, got: {}\n\n",
- cmd, status
- );
- }
- status.success()
-}
-
-fn main() {
+fn main() -> Result<(), ()> {
let config = Arc::new(Config::from_args(env::args().collect()));
// The goal here is to check if the necessary packages are installed, and if not, we
@@ -138,11 +128,24 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
}
}
- try_run(&mut cargo, config.verbose);
+ if try_run(&mut cargo, config.verbose).is_err() {
+ eprintln!("failed to document `{}`", entry.path().display());
+ panic!("Cannot run rustdoc-gui tests");
+ }
}
}
let mut command = Command::new(&config.nodejs);
+
+ if let Ok(current_dir) = env::current_dir() {
+ let local_node_modules = current_dir.join("node_modules");
+ if local_node_modules.exists() {
+ // Link the local node_modules if exists.
+ // This is useful when we run rustdoc-gui-test from outside of the source root.
+ env::set_var("NODE_PATH", local_node_modules);
+ }
+ }
+
command
.arg(config.rust_src.join("src/tools/rustdoc-gui/tester.js"))
.arg("--jobs")
@@ -158,5 +161,5 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
command.args(&config.test_args);
- try_run(&mut command, config.verbose);
+ try_run(&mut command, config.verbose)
}
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index b26480f66..af1bc05dd 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -201,8 +201,8 @@ async function main(argv) {
try {
// This is more convenient that setting fields one by one.
const args = [
- "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error",
- "--allow-file-access-from-files",
+ "--variable", "DOC_PATH", opts["doc_folder"].split("\\").join("/"),
+ "--enable-fail-on-js-error", "--allow-file-access-from-files",
];
if (opts["debug"]) {
debug = true;
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 270704ebf..416517d15 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -22,6 +22,10 @@ function contentToDiffLine(key, value) {
return `"${key}": "${value}",`;
}
+function shouldIgnoreField(fieldName) {
+ return fieldName === "query" || fieldName === "correction";
+}
+
// This function is only called when no matching result was found and therefore will only display
// the diff between the two items.
function betterLookingDiff(entry, data) {
@@ -135,6 +139,9 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
} else if (expected !== null && typeof expected !== "undefined" &&
expected.constructor == Object) { // eslint-disable-line eqeqeq
for (const key in expected) {
+ if (shouldIgnoreField(key)) {
+ continue;
+ }
if (!Object.prototype.hasOwnProperty.call(expected, key)) {
continue;
}
@@ -184,6 +191,9 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) {
const error_text = [];
for (const key in expected) {
+ if (shouldIgnoreField(key)) {
+ continue;
+ }
if (!Object.prototype.hasOwnProperty.call(expected, key)) {
continue;
}
@@ -260,41 +270,49 @@ function checkResult(error_text, loadedFile, displaySuccess) {
return 1;
}
-function runCheck(loadedFile, key, callback) {
- const expected = loadedFile[key];
- const query = loadedFile.QUERY;
-
- if (Array.isArray(query)) {
- if (!Array.isArray(expected)) {
- console.log("FAILED");
- console.log(`==> If QUERY variable is an array, ${key} should be an array too`);
- return 1;
- } else if (query.length !== expected.length) {
- console.log("FAILED");
- console.log(`==> QUERY variable should have the same length as ${key}`);
- return 1;
+function runCheckInner(callback, loadedFile, entry, getCorrections, extra) {
+ if (typeof entry.query !== "string") {
+ console.log("FAILED");
+ console.log("==> Missing `query` field");
+ return false;
+ }
+ let error_text = callback(entry.query, entry, extra ? "[ query `" + entry.query + "`]" : "");
+ if (checkResult(error_text, loadedFile, false) !== 0) {
+ return false;
+ }
+ if (entry.correction !== undefined) {
+ error_text = runCorrections(entry.query, entry.correction, getCorrections, loadedFile);
+ if (checkResult(error_text, loadedFile, false) !== 0) {
+ return false;
}
- for (let i = 0; i < query.length; ++i) {
- const error_text = callback(query[i], expected[i], "[ query `" + query[i] + "`]");
- if (checkResult(error_text, loadedFile, false) !== 0) {
+ }
+ return true;
+}
+
+function runCheck(loadedFile, key, getCorrections, callback) {
+ const expected = loadedFile[key];
+
+ if (Array.isArray(expected)) {
+ for (const entry of expected) {
+ if (!runCheckInner(callback, loadedFile, entry, getCorrections, true)) {
return 1;
}
}
- console.log("OK");
- } else {
- const error_text = callback(query, expected, "");
- if (checkResult(error_text, loadedFile, true) !== 0) {
- return 1;
- }
+ } else if (!runCheckInner(callback, loadedFile, expected, getCorrections, false)) {
+ return 1;
}
+ console.log("OK");
return 0;
}
+function hasCheck(content, checkName) {
+ return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`);
+}
+
function runChecks(testFile, doSearch, parseQuery, getCorrections) {
let checkExpected = false;
let checkParsed = false;
- let checkCorrections = false;
- let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;";
+ let testFileContent = readFile(testFile);
if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
@@ -302,21 +320,17 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) {
testFileContent += "exports.FILTER_CRATE = null;";
}
- if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
+ if (hasCheck(testFileContent, "EXPECTED")) {
testFileContent += "exports.EXPECTED = EXPECTED;";
checkExpected = true;
}
- if (testFileContent.indexOf("\nconst PARSED") !== -1) {
+ if (hasCheck(testFileContent, "PARSED")) {
testFileContent += "exports.PARSED = PARSED;";
checkParsed = true;
}
- if (testFileContent.indexOf("\nconst CORRECTIONS") !== -1) {
- testFileContent += "exports.CORRECTIONS = CORRECTIONS;";
- checkCorrections = true;
- }
- if (!checkParsed && !checkExpected && !checkCorrections) {
+ if (!checkParsed && !checkExpected) {
console.log("FAILED");
- console.log("==> At least `PARSED`, `EXPECTED`, or `CORRECTIONS` is needed!");
+ console.log("==> At least `PARSED` or `EXPECTED` is needed!");
return 1;
}
@@ -324,20 +338,15 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) {
let res = 0;
if (checkExpected) {
- res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => {
+ res += runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => {
return runSearch(query, expected, doSearch, loadedFile, text);
});
}
if (checkParsed) {
- res += runCheck(loadedFile, "PARSED", (query, expected, text) => {
+ res += runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => {
return runParser(query, expected, parseQuery, text);
});
}
- if (checkCorrections) {
- res += runCheck(loadedFile, "CORRECTIONS", (query, expected) => {
- return runCorrections(query, expected, getCorrections, loadedFile);
- });
- }
return res;
}
@@ -367,8 +376,7 @@ function loadSearchJS(doc_folder, resource_suffix) {
},
getCorrections: function(queryStr, filterCrate, currentCrate) {
const parsedQuery = searchModule.parseQuery(queryStr);
- searchModule.execQuery(parsedQuery, searchWords,
- filterCrate, currentCrate);
+ searchModule.execQuery(parsedQuery, searchWords, filterCrate, currentCrate);
return parsedQuery.correction;
},
parseQuery: searchModule.parseQuery,
diff --git a/src/tools/rustfmt/.editorconfig b/src/tools/rustfmt/.editorconfig
index 5bb92df3e..ed6894e5c 100644
--- a/src/tools/rustfmt/.editorconfig
+++ b/src/tools/rustfmt/.editorconfig
@@ -21,6 +21,3 @@ indent_size = unset
indent_style = unset
trim_trailing_whitespace = unset
insert_final_newline = unset
-
-[appveyor.yml]
-end_of_line = unset
diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml
index 25699234a..7dfaa4b92 100644
--- a/src/tools/rustfmt/.github/workflows/upload-assets.yml
+++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml
@@ -46,10 +46,7 @@ jobs:
shell: bash
- name: Build release binaries
- uses: actions-rs/cargo@v1
- with:
- command: build
- args: --release
+ run: cargo build --release
- name: Build archive
shell: bash
diff --git a/src/tools/rustfmt/.travis.yml b/src/tools/rustfmt/.travis.yml
deleted file mode 100644
index d699bd842..000000000
--- a/src/tools/rustfmt/.travis.yml
+++ /dev/null
@@ -1,77 +0,0 @@
-sudo: false
-language: rust
-rust: nightly
-os: linux
-cache:
- directories:
- - $HOME/.cargo
-
-addons:
- apt:
- packages:
- - libcurl4-openssl-dev
- - libelf-dev
- - libdw-dev
-
-matrix:
- include:
- - env: DEPLOY=LINUX
- - env: CFG_RELEASE_CHANNEL=beta
- - os: osx
- - env: INTEGRATION=bitflags
- - env: INTEGRATION=chalk
- - env: INTEGRATION=crater
- - env: INTEGRATION=error-chain
- - env: INTEGRATION=glob
- - env: INTEGRATION=log
- - env: INTEGRATION=mdbook
- - env: INTEGRATION=packed_simd
- - env: INTEGRATION=rust-semverver
- - env: INTEGRATION=stdsimd TARGET=x86_64-unknown-linux-gnu
- - env: INTEGRATION=tempdir
- - env: INTEGRATION=futures-rs
- allow_failures:
- # Using old configuration option
- - env: INTEGRATION=rand
- # Doesn't build - keep this in allow_failures as it's fragile to breaking changes of rustc.
- - env: INTEGRATION=rust-clippy
- # Doesn't build - seems to be because of an option
- - env: INTEGRATION=packed_simd
- # Doesn't build - a temporal build failure due to breaking changes in the nightly compilre
- - env: INTEGRATION=rust-semverver
- # can be moved back to include section after https://github.com/rust-lang-nursery/failure/pull/298 is merged
- - env: INTEGRATION=failure
- # `cargo test` doesn't finish - disabling for now.
- # - env: INTEGRATION=cargo
-
-script:
- - |
- if [ -z ${INTEGRATION} ]; then
- export CFG_RELEASE_CHANNEL=nightly
- export CFG_RELEASE=nightly
- cargo build
- cargo test
- cargo test -- --ignored
- else
- ./ci/integration.sh
- fi
-
-after_success:
-- if [ -z ${INTEGRATION} ]; then travis-cargo coveralls --no-sudo; fi
-
-before_deploy:
- # TODO: cross build
- - cargo build --release --target=x86_64-unknown-linux-gnu
- - tar czf rustfmt-x86_64-unknown-linux-gnu.tar.gz Contributing.md Design.md README.md -C target/x86_64-unknown-linux-gnu/release/rustfmt rustfmt
-
-deploy:
- provider: releases
- api_key:
- secure: "your own encrypted key"
- file:
- - rustfmt-x86_64-unknown-linux-gnu.tar.gz
- on:
- repo: nrc/rustfmt
- tags: true
- condition: "$DEPLOY = LINUX"
- skip_cleanup: true
diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md
index 60f961fa1..fbcd0a57f 100644
--- a/src/tools/rustfmt/CHANGELOG.md
+++ b/src/tools/rustfmt/CHANGELOG.md
@@ -2,6 +2,58 @@
## [Unreleased]
+
+## [1.6.0] 2023-07-02
+
+### Added
+
+- 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)
+[#5684]: https://github.com/rust-lang/rustfmt/issues/5684
+
+## [1.5.3] 2023-06-20
+
+### Fixed
+
+- 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)
+- 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:
+ ```rust
+ /// ```
+ ///
+ /// ```
+ fn main() {}
+ ```
+- rustfmt no longer incorrectly duplicates the where clause bounds when using const expression in where clause bounds with feature `#![feature(generic_const_exprs)]` [#5691](https://github.com/rust-lang/rustfmt/issues/5691). e.g.:
+ ```rust
+ struct S<const C: usize>
+ where
+ [(); { num_slots!(C) }]:, {
+ // code ...
+ }
+ ```
+- Prevent ICE when parsing invalid attributes in `cfg_if!` macros [#5728](https://github.com/rust-lang/rustfmt/issues/5728), [#5729](https://github.com/rust-lang/rustfmt/issues/5729)
+- rustfmt no longer loses comments placed between a doc comment and generic params [#5320](https://github.com/rust-lang/rustfmt/issues/5320)
+- Handle explicit discriminants in enums with comments present [#5686](https://github.com/rust-lang/rustfmt/issues/5686)
+
+### Changed
+
+- Users can now control whether rustc parser errors are displayed with color using rustfmt's `--color` option. To disable colored errors pass `--color=Never` to rustfmt [#5717](https://github.com/rust-lang/rustfmt/issues/5717)
+
+
+### Added
+
+- rustfmt now recognises `+` as the start of a markdown list, and won't incorrectly wrap sublists that begin with `+` when formatting doc comments with `wrap_comments = true` [#5560](https://github.com/rust-lang/rustfmt/pull/5560)
+
+### Misc
+
+- Update various dependencies, including `syn`, `cargo_metadata`, `env_logger`, and `toml`
+
## [1.5.2] 2023-01-24
### Fixed
@@ -56,6 +108,8 @@
- Simplify the rustfmt help text by eliding the full path to the rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214)
+- Bumped the version for serveral dependencies. Most notably `dirs` `v2.0.1` -> `v4.0.0`. This changed the global user config directory on macOS from `$HOME/Library/Preferences` to `$HOME/Library/Application Support` [#5237](https://github.com/rust-lang/rustfmt/pull/5237)
+
### Fixed
- Remove duplicate imports when `imports_granularity` is set to `Item` [#4725](https://github.com/rust-lang/rustfmt/issues/4725)
diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock
index 24166d51c..bd28df7a7 100644
--- a/src/tools/rustfmt/Cargo.lock
+++ b/src/tools/rustfmt/Cargo.lock
@@ -22,23 +22,52 @@ dependencies = [
]
[[package]]
-name = "anyhow"
-version = "1.0.56"
+name = "anstream"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-wincon",
+ "concolor-override",
+ "concolor-query",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
[[package]]
-name = "atty"
-version = "0.2.14"
+name = "anstyle-parse"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.45.0",
]
[[package]]
+name = "anyhow"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+
+[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -88,18 +117,25 @@ dependencies = [
[[package]]
name = "cargo_metadata"
-version = "0.14.2"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
+checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
+ "thiserror",
]
[[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"
@@ -107,53 +143,69 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
-version = "3.1.8"
+version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
+checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
dependencies = [
- "atty",
- "bitflags",
+ "clap_builder",
"clap_derive",
- "indexmap",
- "lazy_static",
- "os_str_bytes",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "bitflags",
+ "clap_lex",
"strsim",
- "termcolor",
- "textwrap",
]
[[package]]
name = "clap_derive"
-version = "3.1.7"
+version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
+checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
dependencies = [
"heck",
- "proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
-name = "crossbeam-utils"
-version = "0.8.8"
+name = "clap_lex"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+
+[[package]]
+name = "concolor-override"
+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 = [
- "cfg-if",
- "lazy_static",
+ "windows-sys 0.45.0",
]
[[package]]
-name = "derive-new"
-version = "0.5.9"
+name = "crossbeam-utils"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "cfg-if",
+ "lazy_static",
]
[[package]]
@@ -211,18 +263,39 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
- "atty",
"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"
@@ -263,9 +336,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
@@ -275,12 +348,9 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "humantime"
@@ -308,15 +378,38 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.8.1"
+version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[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"
@@ -339,9 +432,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.122"
+version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
+checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "libm"
@@ -350,6 +443,12 @@ 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"
+
+[[package]]
name = "log"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -366,18 +465,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "once_cell"
-version = "1.10.0"
+version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
-
-[[package]]
-name = "os_str_bytes"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
-dependencies = [
- "memchr",
-]
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "packed_simd_2"
@@ -390,43 +480,19 @@ dependencies = [
]
[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
-[[package]]
name = "proc-macro2"
-version = "1.0.37"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.17"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@@ -469,12 +535,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
-name = "rustc-workspace-hack"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
-
-[[package]]
name = "rustfmt-config_proc_macro"
version = "0.3.0"
dependencies = [
@@ -485,14 +545,13 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
-version = "1.5.2"
+version = "1.6.0"
dependencies = [
"annotate-snippets",
"anyhow",
"bytecount",
"cargo_metadata",
"clap",
- "derive-new",
"diff",
"dirs",
"env_logger",
@@ -502,7 +561,6 @@ dependencies = [
"lazy_static",
"log",
"regex",
- "rustc-workspace-hack",
"rustfmt-config_proc_macro",
"serde",
"serde_json",
@@ -515,6 +573,20 @@ dependencies = [
]
[[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]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -546,18 +618,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.136"
+version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.136"
+version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
@@ -576,6 +648,15 @@ dependencies = [
]
[[package]]
+name = "serde_spanned"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -583,13 +664,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
-version = "1.0.91"
+version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
+checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
dependencies = [
"proc-macro2",
"quote",
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
@@ -613,25 +694,19 @@ dependencies = [
]
[[package]]
-name = "textwrap"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
-
-[[package]]
name = "thiserror"
-version = "1.0.30"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
@@ -649,14 +724,45 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.8"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
+ "indexmap",
"serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -669,22 +775,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
name = "unicode_categories"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
-name = "version_check"
-version = "0.9.4"
+name = "utf8parse"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "walkdir"
@@ -735,6 +835,147 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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",
+]
+
+[[package]]
+name = "windows-targets"
+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",
+]
+
+[[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"
+
+[[package]]
+name = "winnow"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "yansi-term"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml
index 87ce59d02..8c312f47a 100644
--- a/src/tools/rustfmt/Cargo.toml
+++ b/src/tools/rustfmt/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "rustfmt-nightly"
-version = "1.5.2"
+version = "1.6.0"
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang/rustfmt"
readme = "README.md"
@@ -36,34 +36,28 @@ generic-simd = ["bytecount/generic-simd"]
annotate-snippets = { version = "0.9", features = ["color"] }
anyhow = "1.0"
bytecount = "0.6"
-cargo_metadata = "0.14"
-clap = { version = "3.1", features = ["derive"] }
-derive-new = "0.5"
+cargo_metadata = "0.15.4"
+clap = { version = "4.2.1", features = ["derive"] }
diff = "0.1"
dirs = "4.0"
-env_logger = "0.9"
+env_logger = "0.10.0"
getopts = "0.2"
ignore = "0.4"
itertools = "0.10"
lazy_static = "1.4"
log = "0.4"
regex = "1.5"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0"
term = "0.7"
-thiserror = "1.0"
-toml = "0.5"
+thiserror = "1.0.40"
+toml = "0.7.4"
unicode-segmentation = "1.9"
unicode-width = "0.1"
unicode_categories = "0.1"
rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" }
-# A noop dependency that changes in the Rust repository, it's a bit of a hack.
-# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
-# for more information.
-rustc-workspace-hack = "1.0.0"
-
# Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them.
[package.metadata.rust-analyzer]
diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md
index 49e7e4e64..ac5747800 100644
--- a/src/tools/rustfmt/Configurations.md
+++ b/src/tools/rustfmt/Configurations.md
@@ -2392,6 +2392,78 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi
See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
+## `single_line_let_else_max_width`
+
+Maximum line length for single line let-else statements.
+See the [let-else statement section of the Rust Style Guide](https://github.com/rust-lang/rust/blob/master/src/doc/style-guide/src/statements.md#else-blocks-let-else-statements) for more details on when a let-else statement may be written on a single line.
+A value of `0` (zero) means the divergent `else` block will always be formatted over multiple lines.
+Note this occurs when `use_small_heuristics` is set to `Off`.
+
+By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_let_else_max_width` will take precedence.
+
+- **Default value**: `50`
+- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
+- **Stable**: Yes
+
+#### `50` (default):
+
+```rust
+fn main() {
+ let Some(w) = opt else { return Ok(()) };
+
+ let Some(x) = opt else { return };
+
+ let Some(y) = opt else {
+ return;
+ };
+
+ let Some(z) = some_very_very_very_very_long_name else {
+ return;
+ };
+}
+```
+
+#### `0`:
+
+```rust
+fn main() {
+ let Some(w) = opt else {
+ return Ok(());
+ };
+
+ let Some(x) = opt else {
+ return;
+ };
+
+ let Some(y) = opt else {
+ return;
+ };
+
+ let Some(z) = some_very_very_very_very_long_name else {
+ return;
+ };
+}
+```
+
+#### `100`:
+
+```rust
+fn main() {
+ let Some(w) = opt else { return Ok(()) };
+
+ let Some(x) = opt else { return };
+
+ let Some(y) = opt else {
+ return;
+ };
+
+ let Some(z) = some_very_very_very_very_long_name else { return };
+}
+```
+
+See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
+
+
## `space_after_colon`
Leave a space after the colon.
@@ -2804,6 +2876,7 @@ The ratios are:
* [`array_width`](#array_width) - `60%`
* [`chain_width`](#chain_width) - `60%`
* [`single_line_if_else_max_width`](#single_line_if_else_max_width) - `50%`
+* [`single_line_let_else_max_width`](#single_line_let_else_max_width) - `50%`
For example when `max_width` is set to `100`, the width settings are:
* `fn_call_width=60`
@@ -2813,6 +2886,7 @@ For example when `max_width` is set to `100`, the width settings are:
* `array_width=60`
* `chain_width=60`
* `single_line_if_else_max_width=50`
+* `single_line_let_else_max_width=50`
and when `max_width` is set to `200`:
* `fn_call_width=120`
@@ -2822,6 +2896,7 @@ and when `max_width` is set to `200`:
* `array_width=120`
* `chain_width=120`
* `single_line_if_else_max_width=100`
+* `single_line_let_else_max_width=100`
```rust
enum Lorem {
@@ -2891,6 +2966,7 @@ So if `max_width` is set to `200`, then all the width settings are also set to `
* `array_width=200`
* `chain_width=200`
* `single_line_if_else_max_width=200`
+* `single_line_let_else_max_width=200`
```rust
enum Lorem {
@@ -2918,6 +2994,7 @@ See also:
* [`array_width`](#array_width)
* [`chain_width`](#chain_width)
* [`single_line_if_else_max_width`](#single_line_if_else_max_width)
+* [`single_line_let_else_max_width`](#single_line_let_else_max_width)
## `use_try_shorthand`
@@ -2997,6 +3074,10 @@ See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_s
Break comments to fit on the line
+Note that no wrapping will happen if:
+1. The comment is the start of a markdown header doc comment
+2. An URL was found in the comment
+
- **Default value**: `false`
- **Possible values**: `true`, `false`
- **Stable**: No (tracking issue: [#3347](https://github.com/rust-lang/rustfmt/issues/3347))
@@ -3011,6 +3092,11 @@ Break comments to fit on the line
// commodo consequat.
// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it.
+struct Foo {}
```
#### `true`:
@@ -3021,6 +3107,17 @@ Break comments to fit on the line
// magna aliqua. Ut enim ad minim veniam, quis nostrud
// exercitation ullamco laboris nisi ut aliquip ex ea
// commodo consequat.
+
+// Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+// sed do eiusmod tempor incididunt ut labore et dolore
+// magna aliqua. Ut enim ad minim veniam, quis nostrud
+// exercitation ullamco laboris nisi ut aliquip ex ea
+// commodo consequat.
+
+// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it.
+struct Foo {}
```
# Internal Options
diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md
index 307399601..b986a887c 100644
--- a/src/tools/rustfmt/Contributing.md
+++ b/src/tools/rustfmt/Contributing.md
@@ -91,6 +91,16 @@ Please try to avoid leaving `TODO`s in the code. There are a few around, but I
wish there weren't. You can leave `FIXME`s, preferably with an issue number.
+### Run Rustfmt from source code
+
+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`.
+
+```
+cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml
+```
+
### Version-gate formatting changes
A change that introduces a different code-formatting should be gated on the
diff --git a/src/tools/rustfmt/README.md b/src/tools/rustfmt/README.md
index 0f9652aec..c05184fbb 100644
--- a/src/tools/rustfmt/README.md
+++ b/src/tools/rustfmt/README.md
@@ -1,4 +1,4 @@
-# rustfmt [![Build Status](https://travis-ci.com/rust-lang/rustfmt.svg?branch=master)](https://travis-ci.com/rust-lang/rustfmt) [![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-lang/rustfmt?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/rustfmt) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly) [![Travis Configuration Status](https://img.shields.io/travis/davidalber/rustfmt-travis.svg?label=travis%20example)](https://travis-ci.org/davidalber/rustfmt-travis)
+# rustfmt [![linux](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml) [![mac](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml) [![windows](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly)
A tool for formatting Rust code according to style guidelines.
@@ -7,9 +7,7 @@ If you'd like to help out (and you should, it's a fun project!), see
Conduct](CODE_OF_CONDUCT.md).
You can use rustfmt in Travis CI builds. We provide a minimal Travis CI
-configuration (see [here](#checking-style-on-a-ci-server)) and verify its status
-using another repository. The status of that repository's build is reported by
-the "travis example" badge above.
+configuration (see [here](#checking-style-on-a-ci-server)).
## Quick start
@@ -73,24 +71,6 @@ because in the future Rustfmt might work on code where it currently does not):
fixes to break our stability guarantees).
-## Installation
-
-```sh
-rustup component add rustfmt
-```
-
-## Installing from source
-
-To install from source (nightly required), first checkout to the tag or branch you want to install, then issue
-
-```sh
-cargo install --path .
-```
-
-This will install `rustfmt` in your `~/.cargo/bin`. Make sure to add `~/.cargo/bin` directory to
-your PATH variable.
-
-
## Running
You can run Rustfmt by just typing `rustfmt filename` if you used `cargo
diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.lock b/src/tools/rustfmt/config_proc_macro/Cargo.lock
index 49f2f72a8..626795864 100644
--- a/src/tools/rustfmt/config_proc_macro/Cargo.lock
+++ b/src/tools/rustfmt/config_proc_macro/Cargo.lock
@@ -4,18 +4,18 @@ version = 3
[[package]]
name = "proc-macro2"
-version = "1.0.3"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.2"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@@ -32,18 +32,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.99"
+version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.99"
+version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425"
+checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
@@ -52,17 +52,17 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.5"
+version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
+checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
dependencies = [
"proc-macro2",
"quote",
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
-name = "unicode-xid"
-version = "0.2.0"
+name = "unicode-ident"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml
index d10d0469c..34e8c237f 100644
--- a/src/tools/rustfmt/config_proc_macro/Cargo.toml
+++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml
@@ -13,10 +13,10 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
-syn = { version = "1.0", features = ["full", "visit"] }
+syn = { version = "2.0", features = ["full", "visit"] }
[dev-dependencies]
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0.160", features = ["derive"] }
[features]
default = []
diff --git a/src/tools/rustfmt/config_proc_macro/src/attrs.rs b/src/tools/rustfmt/config_proc_macro/src/attrs.rs
index dd18ff572..d8de9aae0 100644
--- a/src/tools/rustfmt/config_proc_macro/src/attrs.rs
+++ b/src/tools/rustfmt/config_proc_macro/src/attrs.rs
@@ -51,26 +51,26 @@ pub fn is_unstable_variant(attr: &syn::Attribute) -> bool {
}
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
- attr.parse_meta().ok().map_or(false, |meta| match meta {
- syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true,
+ match &attr.meta {
+ syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if path.is_ident(name) => true,
_ => false,
- })
+ }
}
fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool {
- attr.parse_meta().ok().map_or(false, |meta| match meta {
+ match &attr.meta {
syn::Meta::Path(path) if path.is_ident(name) => true,
_ => false,
- })
+ }
}
fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
- attr.parse_meta().ok().and_then(|meta| match meta {
+ match &attr.meta {
syn::Meta::NameValue(syn::MetaNameValue {
- ref path,
- lit: syn::Lit::Str(ref lit_str),
+ path,
+ value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }),
..
}) if path.is_ident(name) => Some(lit_str.value()),
_ => None,
- })
+ }
}
diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain
index 22283b3d6..33ff8b03d 100644
--- a/src/tools/rustfmt/rust-toolchain
+++ b/src/tools/rustfmt/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-01-24"
+channel = "nightly-2023-07-01"
components = ["llvm-tools", "rustc-dev"]
diff --git a/src/tools/rustfmt/src/attr/doc_comment.rs b/src/tools/rustfmt/src/attr/doc_comment.rs
index f653a12a8..25c8158df 100644
--- a/src/tools/rustfmt/src/attr/doc_comment.rs
+++ b/src/tools/rustfmt/src/attr/doc_comment.rs
@@ -2,12 +2,17 @@ use crate::comment::CommentStyle;
use std::fmt::{self, Display};
/// Formats a string as a doc comment using the given [`CommentStyle`].
-#[derive(new)]
pub(super) struct DocCommentFormatter<'a> {
literal: &'a str,
style: CommentStyle<'a>,
}
+impl<'a> DocCommentFormatter<'a> {
+ pub(super) const fn new(literal: &'a str, style: CommentStyle<'a>) -> Self {
+ Self { literal, style }
+ }
+}
+
impl Display for DocCommentFormatter<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let opener = self.style.opener().trim_end();
diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 47846424b..03b75c1b0 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/src/bin/main.rs
@@ -84,7 +84,7 @@ pub enum OperationError {
#[error("{0}")]
IoError(IoError),
/// Attempt to use --emit with a mode which is not currently
- /// supported with stdandard input.
+ /// supported with standard input.
#[error("Emit mode {0} not supported with standard output.")]
StdinBadEmit(EmitMode),
}
diff --git a/src/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs
index 2b714b68d..bc9745275 100644
--- a/src/tools/rustfmt/src/cargo-fmt/main.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/main.rs
@@ -14,7 +14,8 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;
-use clap::{AppSettings, CommandFactory, Parser};
+use cargo_metadata::Edition;
+use clap::{CommandFactory, Parser};
#[path = "test/mod.rs"]
#[cfg(test)]
@@ -22,7 +23,7 @@ mod cargo_fmt_tests;
#[derive(Parser)]
#[clap(
- global_setting(AppSettings::NoAutoVersion),
+ disable_version_flag = true,
bin_name = "cargo fmt",
about = "This utility formats all bin and lib files of \
the current crate using rustfmt."
@@ -45,7 +46,7 @@ pub struct Opts {
short = 'p',
long = "package",
value_name = "package",
- multiple_values = true
+ num_args = 1..
)]
packages: Vec<String>,
@@ -270,7 +271,7 @@ pub struct Target {
/// A kind of target (e.g., lib, bin, example, ...).
kind: String,
/// Rust edition for this target.
- edition: String,
+ edition: Edition,
}
impl Target {
@@ -281,7 +282,7 @@ impl Target {
Target {
path: canonicalized,
kind: target.kind[0].clone(),
- edition: target.edition.clone(),
+ edition: target.edition,
}
}
}
@@ -506,7 +507,7 @@ fn run_rustfmt(
let mut command = rustfmt_command()
.stdout(stdout)
.args(files)
- .args(&["--edition", edition])
+ .args(&["--edition", edition.as_str()])
.args(fmt_args)
.spawn()
.map_err(|e| match e.kind() {
diff --git a/src/tools/rustfmt/src/cargo-fmt/test/targets.rs b/src/tools/rustfmt/src/cargo-fmt/test/targets.rs
index b7e7fabdf..34accb213 100644
--- a/src/tools/rustfmt/src/cargo-fmt/test/targets.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/test/targets.rs
@@ -2,7 +2,7 @@ use super::*;
struct ExpTarget {
path: &'static str,
- edition: &'static str,
+ edition: Edition,
kind: &'static str,
}
@@ -26,7 +26,7 @@ mod all_targets {
for target in exp_targets {
assert!(targets.contains(&Target {
path: get_path(target.path),
- edition: target.edition.to_owned(),
+ edition: target.edition,
kind: target.kind.to_owned(),
}));
}
@@ -39,17 +39,17 @@ mod all_targets {
let exp_targets = vec![
ExpTarget {
path: "dependency-dir-name/subdep-dir-name/src/lib.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "lib",
},
ExpTarget {
path: "dependency-dir-name/src/lib.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "lib",
},
ExpTarget {
path: "src/main.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "main",
},
];
@@ -79,32 +79,32 @@ mod all_targets {
let exp_targets = vec![
ExpTarget {
path: "ws/a/src/main.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "bin",
},
ExpTarget {
path: "ws/b/src/main.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "bin",
},
ExpTarget {
path: "ws/c/src/lib.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "lib",
},
ExpTarget {
path: "ws/a/d/src/lib.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "lib",
},
ExpTarget {
path: "e/src/main.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "main",
},
ExpTarget {
path: "ws/a/d/f/src/lib.rs",
- edition: "2018",
+ edition: Edition::E2018,
kind: "lib",
},
];
diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs
index 4d565afc1..85918ecc1 100644
--- a/src/tools/rustfmt/src/comment.rs
+++ b/src/tools/rustfmt/src/comment.rs
@@ -432,12 +432,18 @@ impl CodeBlockAttribute {
/// Block that is formatted as an item.
///
-/// An item starts with either a star `*` a dash `-` or a greater-than `>`.
+/// An item starts with either a star `*`, a dash `-`, a greater-than `>`, a plus '+', or a number
+/// `12.` or `34)` (with at most 2 digits). An item represents CommonMark's ["list
+/// items"](https://spec.commonmark.org/0.30/#list-items) and/or ["block
+/// quotes"](https://spec.commonmark.org/0.30/#block-quotes), but note that only a subset of
+/// CommonMark is recognized - see the doc comment of [`ItemizedBlock::get_marker_length`] for more
+/// details.
+///
/// Different level of indentation are handled by shrinking the shape accordingly.
struct ItemizedBlock {
/// the lines that are identified as part of an itemized block
lines: Vec<String>,
- /// the number of characters (typically whitespaces) up to the item sigil
+ /// the number of characters (typically whitespaces) up to the item marker
indent: usize,
/// the string that marks the start of an item
opener: String,
@@ -446,36 +452,70 @@ struct ItemizedBlock {
}
impl ItemizedBlock {
- /// Returns `true` if the line is formatted as an item
- fn is_itemized_line(line: &str) -> bool {
- let trimmed = line.trim_start();
- trimmed.starts_with("* ") || trimmed.starts_with("- ") || trimmed.starts_with("> ")
- }
-
- /// Creates a new ItemizedBlock described with the given line.
- /// The `is_itemized_line` needs to be called first.
- fn new(line: &str) -> ItemizedBlock {
- let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
- // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized
- // content formatted correctly.
- let mut indent = space_to_sigil + 2;
+ /// Checks whether the `trimmed` line includes an item marker. Returns `None` if there is no
+ /// marker. Returns the length of the marker (in bytes) if one is present. Note that the length
+ /// includes the whitespace that follows the marker, for example the marker in `"* list item"`
+ /// has the length of 2.
+ ///
+ /// This function recognizes item markers that correspond to CommonMark's
+ /// ["bullet list marker"](https://spec.commonmark.org/0.30/#bullet-list-marker),
+ /// ["block quote marker"](https://spec.commonmark.org/0.30/#block-quote-marker), and/or
+ /// ["ordered list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker).
+ ///
+ /// Compared to CommonMark specification, the number of digits that are allowed in an ["ordered
+ /// list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker) is more limited (to at
+ /// most 2 digits). Limiting the length of the marker helps reduce the risk of recognizing
+ /// arbitrary numbers as markers. See also
+ /// <https://talk.commonmark.org/t/blank-lines-before-lists-revisited/1990> which gives the
+ /// following example where a number (i.e. "1868") doesn't signify an ordered list:
+ /// ```md
+ /// The Captain died in
+ /// 1868. He wes buried in...
+ /// ```
+ fn get_marker_length(trimmed: &str) -> Option<usize> {
+ // https://spec.commonmark.org/0.30/#bullet-list-marker or
+ // https://spec.commonmark.org/0.30/#block-quote-marker
+ let itemized_start = ["* ", "- ", "> ", "+ "];
+ if itemized_start.iter().any(|s| trimmed.starts_with(s)) {
+ return Some(2); // All items in `itemized_start` have length 2.
+ }
+
+ // https://spec.commonmark.org/0.30/#ordered-list-marker, where at most 2 digits are
+ // 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)) {
+ return Some(prefix.len() + suffix.len());
+ }
+ }
+ }
+
+ None // No markers found.
+ }
+
+ /// Creates a new `ItemizedBlock` described with the given `line`.
+ /// Returns `None` if `line` doesn't start an item.
+ fn new(line: &str) -> Option<ItemizedBlock> {
+ let marker_length = ItemizedBlock::get_marker_length(line.trim_start())?;
+ let space_to_marker = line.chars().take_while(|c| c.is_whitespace()).count();
+ let mut indent = space_to_marker + marker_length;
let mut line_start = " ".repeat(indent);
// Markdown blockquote start with a "> "
if line.trim_start().starts_with(">") {
// remove the original +2 indent because there might be multiple nested block quotes
// and it's easier to reason about the final indent by just taking the length
- // of th new line_start. We update the indent because it effects the max width
+ // of the new line_start. We update the indent because it effects the max width
// of each formatted line.
line_start = itemized_block_quote_start(line, line_start, 2);
indent = line_start.len();
}
- ItemizedBlock {
+ Some(ItemizedBlock {
lines: vec![line[indent..].to_string()],
indent,
opener: line[..indent].to_string(),
line_start,
- }
+ })
}
/// Returns a `StringFormat` used for formatting the content of an item.
@@ -494,7 +534,7 @@ impl ItemizedBlock {
/// Returns `true` if the line is part of the current itemized block.
/// If it is, then it is added to the internal lines list.
fn add_line(&mut self, line: &str) -> bool {
- if !ItemizedBlock::is_itemized_line(line)
+ if ItemizedBlock::get_marker_length(line.trim_start()).is_none()
&& self.indent <= line.chars().take_while(|c| c.is_whitespace()).count()
{
self.lines.push(line.to_string());
@@ -726,7 +766,7 @@ impl<'a> CommentRewrite<'a> {
let code_block = match self.code_block_attr.as_ref().unwrap() {
CodeBlockAttribute::Rust
if self.fmt.config.format_code_in_doc_comments()
- && !self.code_block_buffer.is_empty() =>
+ && !self.code_block_buffer.trim().is_empty() =>
{
let mut config = self.fmt.config.clone();
config.set().wrap_comments(false);
@@ -765,10 +805,11 @@ impl<'a> CommentRewrite<'a> {
self.item_block = None;
if let Some(stripped) = line.strip_prefix("```") {
self.code_block_attr = Some(CodeBlockAttribute::new(stripped))
- } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(line) {
- let ib = ItemizedBlock::new(line);
- self.item_block = Some(ib);
- return false;
+ } else if self.fmt.config.wrap_comments() {
+ if let Some(ib) = ItemizedBlock::new(line) {
+ self.item_block = Some(ib);
+ return false;
+ }
}
if self.result == self.opener {
@@ -801,10 +842,13 @@ impl<'a> CommentRewrite<'a> {
// 2) The comment is not the start of a markdown header doc comment
// 3) The comment width exceeds the shape's width
// 4) No URLS were found in the comment
+ // If this changes, the documentation in ../Configurations.md#wrap_comments
+ // should be changed accordingly.
let should_wrap_comment = self.fmt.config.wrap_comments()
&& !is_markdown_header_doc_comment
&& unicode_str_width(line) > self.fmt.shape.width
- && !has_url(line);
+ && !has_url(line)
+ && !is_table_item(line);
if should_wrap_comment {
match rewrite_string(line, &self.fmt, self.max_width) {
@@ -939,6 +983,18 @@ fn has_url(s: &str) -> bool {
|| REFERENCE_LINK_URL.is_match(s)
}
+/// Returns true if the given string may be part of a Markdown table.
+fn is_table_item(mut s: &str) -> bool {
+ // This function may return false positive, but should get its job done in most cases (i.e.
+ // markdown tables with two column delimiters).
+ s = s.trim_start();
+ return s.starts_with('|')
+ && match s.rfind('|') {
+ Some(0) | None => false,
+ _ => true,
+ };
+}
+
/// Given the span, rewrite the missing comment inside it if available.
/// Note that the given span must only include comments (or leading/trailing whitespaces).
pub(crate) fn rewrite_missing_comment(
@@ -2004,4 +2060,96 @@ fn main() {
"#;
assert_eq!(s, filter_normal_code(s_with_comment));
}
+
+ #[test]
+ fn test_itemized_block_first_line_handling() {
+ fn run_test(
+ test_input: &str,
+ expected_line: &str,
+ expected_indent: usize,
+ expected_opener: &str,
+ 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!(
+ expected_line_start, &block.line_start,
+ "test_input: {:?}",
+ test_input
+ );
+ }
+
+ run_test("- foo", "foo", 2, "- ", " ");
+ run_test("* foo", "foo", 2, "* ", " ");
+ run_test("> foo", "foo", 2, "> ", "> ");
+
+ run_test("1. foo", "foo", 3, "1. ", " ");
+ run_test("12. foo", "foo", 4, "12. ", " ");
+ run_test("1) foo", "foo", 3, "1) ", " ");
+ run_test("12) foo", "foo", 4, "12) ", " ");
+
+ run_test(" - foo", "foo", 6, " - ", " ");
+
+ // https://spec.commonmark.org/0.30 says: "A start number may begin with 0s":
+ run_test("0. foo", "foo", 3, "0. ", " ");
+ run_test("01. foo", "foo", 4, "01. ", " ");
+ }
+
+ #[test]
+ fn test_itemized_block_nonobvious_markers_are_rejected() {
+ let test_inputs = vec![
+ // Non-numeric item markers (e.g. `a.` or `iv.`) are not allowed by
+ // https://spec.commonmark.org/0.30/#ordered-list-marker. We also note that allowing
+ // them would risk misidentifying regular words as item markers. See also the
+ // discussion in https://talk.commonmark.org/t/blank-lines-before-lists-revisited/1990
+ "word. rest of the paragraph.",
+ "a. maybe this is a list item? maybe not?",
+ "iv. maybe this is a list item? maybe not?",
+ // Numbers with 3 or more digits are not recognized as item markers, to avoid
+ // formatting the following example as a list:
+ //
+ // ```
+ // The Captain died in
+ // 1868. He was buried in...
+ // ```
+ "123. only 2-digit numbers are recognized as item markers.",
+ // Parens:
+ "123) giving some coverage to parens as well.",
+ "a) giving some coverage to parens as well.",
+ // https://spec.commonmark.org/0.30 says that "at least one space or tab is needed
+ // between the list marker and any following content":
+ "1.Not a list item.",
+ "1.2.3. Not a list item.",
+ "1)Not a list item.",
+ "-Not a list item.",
+ "+Not a list item.",
+ "+1 not a list item.",
+ // https://spec.commonmark.org/0.30 says: "A start number may not be negative":
+ "-1. Not a list item.",
+ "-1 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
+ );
+ }
+ }
}
diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs
index 54ca7676d..c836b4bbb 100644
--- a/src/tools/rustfmt/src/config/config_type.rs
+++ b/src/tools/rustfmt/src/config/config_type.rs
@@ -121,6 +121,7 @@ macro_rules! create_config {
| "use_small_heuristics"
| "fn_call_width"
| "single_line_if_else_max_width"
+ | "single_line_let_else_max_width"
| "attr_fn_like_width"
| "struct_lit_width"
| "struct_variant_width"
@@ -269,6 +270,7 @@ macro_rules! create_config {
| "use_small_heuristics"
| "fn_call_width"
| "single_line_if_else_max_width"
+ | "single_line_let_else_max_width"
| "attr_fn_like_width"
| "struct_lit_width"
| "struct_variant_width"
@@ -407,6 +409,14 @@ macro_rules! create_config {
"single_line_if_else_max_width",
);
self.single_line_if_else_max_width.2 = single_line_if_else_max_width;
+
+ let single_line_let_else_max_width = get_width_value(
+ self.was_set().single_line_let_else_max_width(),
+ self.single_line_let_else_max_width.2,
+ heuristics.single_line_let_else_max_width,
+ "single_line_let_else_max_width",
+ );
+ self.single_line_let_else_max_width.2 = single_line_let_else_max_width;
}
fn set_heuristics(&mut self) {
diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs
index 14f27f3f8..6f41b299e 100644
--- a/src/tools/rustfmt/src/config/mod.rs
+++ b/src/tools/rustfmt/src/config/mod.rs
@@ -58,6 +58,9 @@ create_config! {
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \
expressions. A value of zero means always break if-else expressions.";
+ single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \
+ let-else statements. A value of zero means always format the divergent `else` block \
+ over multiple lines.";
// Comments. macros, and strings
wrap_comments: bool, false, false, "Break comments to fit on the line";
@@ -473,6 +476,9 @@ mod test {
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \
line if-else expressions. A value of zero means always break if-else expressions.";
+ single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \
+ line let-else statements. A value of zero means always format the divergent \
+ `else` block over multiple lines.";
// Options that are used by the tests
stable_option: bool, false, true, "A stable option";
@@ -619,6 +625,7 @@ struct_variant_width = 35
array_width = 60
chain_width = 60
single_line_if_else_max_width = 50
+single_line_let_else_max_width = 50
wrap_comments = false
format_code_in_doc_comments = false
doc_comment_code_block_width = 100
diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs
index 257a17b27..3aa1a4de9 100644
--- a/src/tools/rustfmt/src/config/options.rs
+++ b/src/tools/rustfmt/src/config/options.rs
@@ -18,7 +18,7 @@ pub enum NewlineStyle {
Auto,
/// Force CRLF (`\r\n`).
Windows,
- /// Force CR (`\n).
+ /// Force CR (`\n`).
Unix,
/// `\r\n` in Windows, `\n` on other platforms.
Native,
@@ -236,6 +236,9 @@ pub struct WidthHeuristics {
// Maximum line length for single line if-else expressions. A value
// of zero means always break if-else expressions.
pub(crate) single_line_if_else_max_width: usize,
+ // Maximum line length for single line let-else statements. A value of zero means
+ // always format the divergent `else` block over multiple lines.
+ pub(crate) single_line_let_else_max_width: usize,
}
impl fmt::Display for WidthHeuristics {
@@ -255,6 +258,7 @@ impl WidthHeuristics {
array_width: usize::max_value(),
chain_width: usize::max_value(),
single_line_if_else_max_width: 0,
+ single_line_let_else_max_width: 0,
}
}
@@ -267,6 +271,7 @@ impl WidthHeuristics {
array_width: max_width,
chain_width: max_width,
single_line_if_else_max_width: max_width,
+ single_line_let_else_max_width: max_width,
}
}
@@ -288,6 +293,7 @@ impl WidthHeuristics {
array_width: (60.0 * max_width_ratio).round() as usize,
chain_width: (60.0 * max_width_ratio).round() as usize,
single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
+ single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize,
}
}
}
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 5dc628adb..5b1b4fbd4 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -232,6 +232,7 @@ pub(crate) fn format_expr(
ast::ExprKind::Ret(Some(ref expr)) => {
rewrite_unary_prefix(context, "return ", &**expr, shape)
}
+ ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape),
ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()),
ast::ExprKind::Yeet(Some(ref expr)) => {
rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
@@ -576,6 +577,17 @@ fn rewrite_block(
context: &RewriteContext<'_>,
shape: Shape,
) -> Option<String> {
+ rewrite_block_inner(block, attrs, label, true, context, shape)
+}
+
+fn rewrite_block_inner(
+ block: &ast::Block,
+ attrs: Option<&[ast::Attribute]>,
+ label: Option<ast::Label>,
+ allow_single_line: bool,
+ context: &RewriteContext<'_>,
+ shape: Shape,
+) -> Option<String> {
let prefix = block_prefix(context, block, shape)?;
// shape.width is used only for the single line case: either the empty block `{}`,
@@ -586,7 +598,7 @@ fn rewrite_block(
let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true);
if let Some(ref result_str) = result {
- if result_str.lines().count() <= 3 {
+ if allow_single_line && result_str.lines().count() <= 3 {
if let rw @ Some(_) =
rewrite_single_line_block(context, &prefix, block, attrs, label, shape)
{
@@ -598,6 +610,16 @@ fn rewrite_block(
result
}
+/// Rewrite the divergent block of a `let-else` statement.
+pub(crate) fn rewrite_let_else_block(
+ block: &ast::Block,
+ allow_single_line: bool,
+ context: &RewriteContext<'_>,
+ shape: Shape,
+) -> Option<String> {
+ rewrite_block_inner(block, None, None, allow_single_line, context, shape)
+}
+
// Rewrite condition if the given expression has one.
pub(crate) fn rewrite_cond(
context: &RewriteContext<'_>,
@@ -1004,6 +1026,49 @@ impl<'a> ControlFlow<'a> {
}
}
+/// Rewrite the `else` keyword with surrounding comments.
+///
+/// force_newline_else: whether or not to rewrite the `else` keyword on a newline.
+/// is_last: true if this is an `else` and `false` if this is an `else if` block.
+/// context: rewrite context
+/// span: Span between the end of the last expression and the start of the else block,
+/// which contains the `else` keyword
+/// shape: Shape
+pub(crate) fn rewrite_else_kw_with_comments(
+ force_newline_else: bool,
+ is_last: bool,
+ context: &RewriteContext<'_>,
+ span: Span,
+ shape: Shape,
+) -> String {
+ let else_kw_lo = context.snippet_provider.span_before(span, "else");
+ let before_else_kw = mk_sp(span.lo(), else_kw_lo);
+ let before_else_kw_comment = extract_comment(before_else_kw, context, shape);
+
+ let else_kw_hi = context.snippet_provider.span_after(span, "else");
+ let after_else_kw = mk_sp(else_kw_hi, span.hi());
+ let after_else_kw_comment = extract_comment(after_else_kw, context, shape);
+
+ let newline_sep = &shape.indent.to_string_with_newline(context.config);
+ let before_sep = match context.config.control_brace_style() {
+ _ if force_newline_else => newline_sep.as_ref(),
+ ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => {
+ newline_sep.as_ref()
+ }
+ ControlBraceStyle::AlwaysSameLine => " ",
+ };
+ let after_sep = match context.config.control_brace_style() {
+ ControlBraceStyle::AlwaysNextLine if is_last => newline_sep.as_ref(),
+ _ => " ",
+ };
+
+ format!(
+ "{}else{}",
+ before_else_kw_comment.as_ref().map_or(before_sep, |s| &**s),
+ after_else_kw_comment.as_ref().map_or(after_sep, |s| &**s),
+ )
+}
+
impl<'a> Rewrite for ControlFlow<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
@@ -1070,41 +1135,14 @@ impl<'a> Rewrite for ControlFlow<'a> {
}
};
- let between_kwd_else_block = mk_sp(
- self.block.span.hi(),
- context
- .snippet_provider
- .span_before(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
- );
- let between_kwd_else_block_comment =
- extract_comment(between_kwd_else_block, context, shape);
-
- let after_else = mk_sp(
- context
- .snippet_provider
- .span_after(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
- else_block.span.lo(),
+ let else_kw = rewrite_else_kw_with_comments(
+ false,
+ last_in_chain,
+ context,
+ self.block.span.between(else_block.span),
+ shape,
);
- let after_else_comment = extract_comment(after_else, context, shape);
-
- let between_sep = match context.config.control_brace_style() {
- ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => {
- &*alt_block_sep
- }
- ControlBraceStyle::AlwaysSameLine => " ",
- };
- let after_sep = match context.config.control_brace_style() {
- ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep,
- _ => " ",
- };
-
- result.push_str(&format!(
- "{}else{}",
- between_kwd_else_block_comment
- .as_ref()
- .map_or(between_sep, |s| &**s),
- after_else_comment.as_ref().map_or(after_sep, |s| &**s),
- ));
+ result.push_str(&else_kw);
result.push_str(&rewrite?);
}
@@ -1724,9 +1762,11 @@ pub(crate) fn rewrite_field(
let overhead = name.len() + separator.len();
let expr_shape = shape.offset_left(overhead)?;
let expr = field.expr.rewrite(context, expr_shape);
-
+ let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_));
match expr {
- Some(ref e) if e.as_str() == name && context.config.use_field_init_shorthand() => {
+ Some(ref e)
+ if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() =>
+ {
Some(attrs_str + name)
}
Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs
index 1dfd8a514..1f4ad6960 100644
--- a/src/tools/rustfmt/src/formatting.rs
+++ b/src/tools/rustfmt/src/formatting.rs
@@ -175,7 +175,6 @@ fn format_project<T: FormatHandler>(
}
// Used for formatting files.
-#[derive(new)]
struct FormatContext<'a, T: FormatHandler> {
krate: &'a ast::Crate,
report: FormatReport,
@@ -185,6 +184,22 @@ struct FormatContext<'a, T: FormatHandler> {
}
impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
+ fn new(
+ krate: &'a ast::Crate,
+ report: FormatReport,
+ parse_session: ParseSess,
+ config: &'a Config,
+ handler: &'a mut T,
+ ) -> Self {
+ FormatContext {
+ krate,
+ report,
+ parse_session,
+ config,
+ handler,
+ }
+ }
+
fn ignore_file(&self, path: &FileName) -> bool {
self.parse_session.ignore_file(path)
}
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 43779cfae..d5bc38303 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -18,7 +18,8 @@ use crate::config::lists::*;
use crate::config::{BraceStyle, Config, IndentStyle, Version};
use crate::expr::{
is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with,
- rewrite_assign_rhs_with_comments, RhsAssignKind, RhsTactics,
+ rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block,
+ RhsAssignKind, RhsTactics,
};
use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
use crate::macros::{rewrite_macro, MacroPosition};
@@ -44,7 +45,7 @@ fn type_annotation_separator(config: &Config) -> &str {
}
// Statements of the form
-// let pat: ty = init;
+// let pat: ty = init; or let pat: ty = init else { .. };
impl Rewrite for ast::Local {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
debug!(
@@ -54,7 +55,7 @@ impl Rewrite for ast::Local {
skip_out_of_file_lines_range!(context, self.span);
- if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) {
+ if contains_skip(&self.attrs) {
return None;
}
@@ -112,7 +113,7 @@ impl Rewrite for ast::Local {
result.push_str(&infix);
- if let Some((init, _els)) = self.kind.init_else_opt() {
+ if let Some((init, else_block)) = self.kind.init_else_opt() {
// 1 = trailing semicolon;
let nested_shape = shape.sub_width(1)?;
@@ -123,7 +124,49 @@ impl Rewrite for ast::Local {
&RhsAssignKind::Expr(&init.kind, init.span),
nested_shape,
)?;
- // todo else
+
+ if let Some(block) = else_block {
+ let else_kw_span = init.span.between(block.span);
+ let force_newline_else = pat_str.contains('\n')
+ || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape);
+ let else_kw = rewrite_else_kw_with_comments(
+ force_newline_else,
+ true,
+ context,
+ else_kw_span,
+ shape,
+ );
+ result.push_str(&else_kw);
+
+ // At this point we've written `let {pat} = {expr} else' into the buffer, and we
+ // want to calculate up front if there's room to write the divergent block on the
+ // same line. The available space varies based on indentation so we clamp the width
+ // on the smaller of `shape.width` and `single_line_let_else_max_width`.
+ let max_width =
+ 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 allow_single_line = !force_newline_else
+ && available_space > 0
+ && allow_single_line_let_else_block(&result, block);
+
+ let mut rw_else_block =
+ rewrite_let_else_block(block, allow_single_line, context, shape)?;
+
+ let single_line_else = !rw_else_block.contains('\n');
+ // +1 for the trailing `;`
+ let else_block_exceeds_width = rw_else_block.len() + 1 > available_space;
+
+ if allow_single_line && single_line_else && else_block_exceeds_width {
+ // writing this on one line would exceed the available width
+ // so rewrite the else block over multiple lines.
+ rw_else_block = rewrite_let_else_block(block, false, context, shape)?;
+ }
+
+ result.push_str(&rw_else_block);
+ };
}
result.push(';');
@@ -131,6 +174,61 @@ impl Rewrite for ast::Local {
}
}
+/// When the initializer expression is multi-lined, then the else keyword and opening brace of the
+/// block ( i.e. "else {") should be put on the same line as the end of the initializer expression
+/// if all the following are true:
+///
+/// 1. The initializer expression ends with one or more closing parentheses, square brackets,
+/// or braces
+/// 2. There is nothing else on that line
+/// 3. That line is not indented beyond the indent on the first line of the let keyword
+fn same_line_else_kw_and_brace(
+ init_str: &str,
+ context: &RewriteContext<'_>,
+ else_kw_span: Span,
+ init_shape: Shape,
+) -> bool {
+ if !init_str.contains('\n') {
+ // initializer expression is single lined. The "else {" can only be placed on the same line
+ // as the initializer expression if there is enough room for it.
+ // 7 = ` else {`
+ return init_shape.width.saturating_sub(init_str.len()) >= 7;
+ }
+
+ // 1. The initializer expression ends with one or more `)`, `]`, `}`.
+ if !init_str.ends_with([')', ']', '}']) {
+ return false;
+ }
+
+ // 2. There is nothing else on that line
+ // For example, there are no comments
+ let else_kw_snippet = context.snippet(else_kw_span).trim();
+ if else_kw_snippet != "else" {
+ return false;
+ }
+
+ // 3. The last line of the initializer expression is not indented beyond the `let` keyword
+ let indent = init_shape.indent.to_string(context.config);
+ init_str
+ .lines()
+ .last()
+ .expect("initializer expression is multi-lined")
+ .strip_prefix(indent.as_ref())
+ .map_or(false, |l| !l.starts_with(char::is_whitespace))
+}
+
+fn allow_single_line_let_else_block(result: &str, block: &ast::Block) -> bool {
+ if result.contains('\n') {
+ return false;
+ }
+
+ if block.stmts.len() <= 1 {
+ return true;
+ }
+
+ false
+}
+
// FIXME convert to using rewrite style rather than visitor
// FIXME format modules in this style
#[allow(dead_code)]
@@ -560,7 +658,7 @@ impl<'a> FmtVisitor<'a> {
let variant_body = match field.data {
ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct(
&context,
- &StructParts::from_variant(field),
+ &StructParts::from_variant(field, &context),
self.block_indent,
Some(one_line_width),
)?,
@@ -951,14 +1049,14 @@ impl<'a> StructParts<'a> {
format_header(context, self.prefix, self.ident, self.vis, offset)
}
- fn from_variant(variant: &'a ast::Variant) -> Self {
+ fn from_variant(variant: &'a ast::Variant, context: &RewriteContext<'_>) -> Self {
StructParts {
prefix: "",
ident: variant.ident,
vis: &DEFAULT_VISIBILITY,
def: &variant.data,
generics: None,
- span: variant.span,
+ span: enum_variant_span(variant, context),
}
}
@@ -979,6 +1077,25 @@ impl<'a> StructParts<'a> {
}
}
+fn enum_variant_span(variant: &ast::Variant, context: &RewriteContext<'_>) -> Span {
+ use ast::VariantData::*;
+ if let Some(ref anon_const) = variant.disr_expr {
+ let span_before_consts = variant.span.until(anon_const.value.span);
+ let hi = match &variant.data {
+ Struct(..) => context
+ .snippet_provider
+ .span_after_last(span_before_consts, "}"),
+ Tuple(..) => context
+ .snippet_provider
+ .span_after_last(span_before_consts, ")"),
+ Unit(..) => variant.ident.span.hi(),
+ };
+ mk_sp(span_before_consts.lo(), hi)
+ } else {
+ variant.span
+ }
+}
+
fn format_struct(
context: &RewriteContext<'_>,
struct_parts: &StructParts<'_>,
@@ -1278,7 +1395,7 @@ pub(crate) fn format_struct_struct(
let header_hi = struct_parts.ident.span.hi();
let body_lo = if let Some(generics) = struct_parts.generics {
// Adjust the span to start at the end of the generic arguments before searching for the '{'
- let span = span.with_lo(generics.span.hi());
+ let span = span.with_lo(generics.where_clause.span.hi());
context.snippet_provider.span_after(span, "{")
} else {
context.snippet_provider.span_after(span, "{")
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index b27405efd..567628f63 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -5,8 +5,6 @@
#![allow(clippy::match_like_matches_macro)]
#![allow(unreachable_pub)]
-#[macro_use]
-extern crate derive_new;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 7978d8cba..e9a298a27 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -1119,12 +1119,15 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D
// A very simple parser that just parses a macros 2.0 definition into its branches.
// Currently we do not attempt to parse any further than that.
-#[derive(new)]
struct MacroParser {
toks: TokenTreeCursor,
}
impl MacroParser {
+ const fn new(toks: TokenTreeCursor) -> Self {
+ Self { toks }
+ }
+
// (`(` ... `)` `=>` `{` ... `}`)*
fn parse(&mut self) -> Option<Macro> {
let mut branches = vec![];
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index af0b95430..d81bf24db 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -589,6 +589,8 @@ impl<'a> Context<'a> {
fn rewrite_items(&self) -> Option<(bool, String)> {
let span = self.items_span();
+ debug!("items: {:?}", self.items);
+
let items = itemize_list(
self.context.snippet_provider,
self.items.iter(),
@@ -603,6 +605,8 @@ impl<'a> Context<'a> {
);
let mut list_items: Vec<_> = items.collect();
+ debug!("items: {list_items:?}");
+
// Try letting the last argument overflow to the next line with block
// indentation. If its first line fits on one line with the other arguments,
// we format the function arguments horizontally.
diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs
index d1c75126e..d135da7e3 100644
--- a/src/tools/rustfmt/src/pairs.rs
+++ b/src/tools/rustfmt/src/pairs.rs
@@ -9,7 +9,7 @@ use crate::utils::{
};
/// Sigils that decorate a binop pair.
-#[derive(new, Clone, Copy)]
+#[derive(Clone, Copy)]
pub(crate) struct PairParts<'a> {
prefix: &'a str,
infix: &'a str,
@@ -17,6 +17,13 @@ pub(crate) struct PairParts<'a> {
}
impl<'a> PairParts<'a> {
+ pub(crate) const fn new(prefix: &'a str, infix: &'a str, suffix: &'a str) -> Self {
+ Self {
+ prefix,
+ infix,
+ suffix,
+ }
+ }
pub(crate) fn infix(infix: &'a str) -> PairParts<'a> {
PairParts {
prefix: "",
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index ace1a76b3..cbc4c90b8 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -34,6 +34,11 @@ fn parse_cfg_if_inner<'a>(
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}
+
+ if !matches!(parser.token.kind, TokenKind::Pound) {
+ return Err("Failed to parse attributes");
+ }
+
// Inner attributes are not actually syntactically permitted here, but we don't
// care about inner vs outer attributes in this position. Our purpose with this
// special case parsing of cfg_if macros is to ensure we can correctly resolve
@@ -44,7 +49,10 @@ fn parse_cfg_if_inner<'a>(
// See also https://github.com/rust-lang/rust/pull/79433
parser
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
- .map_err(|_| "Failed to parse attributes")?;
+ .map_err(|e| {
+ e.cancel();
+ "Failed to parse attributes"
+ })?;
}
if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index a64963db6..81b5015dd 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -12,6 +12,7 @@ use rustc_span::{
};
use crate::config::file_lines::LineRange;
+use crate::config::options::Color;
use crate::ignore_path::IgnorePathSet;
use crate::parse::parser::{ModError, ModulePathSuccess};
use crate::source_map::LineRangeUtils;
@@ -107,15 +108,26 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
}
}
+impl From<Color> for ColorConfig {
+ fn from(color: Color) -> Self {
+ match color {
+ Color::Auto => ColorConfig::Auto,
+ Color::Always => ColorConfig::Always,
+ Color::Never => ColorConfig::Never,
+ }
+ }
+}
+
fn default_handler(
source_map: Lrc<SourceMap>,
ignore_path_set: Lrc<IgnorePathSet>,
can_reset: Lrc<AtomicBool>,
hide_parse_errors: bool,
+ color: Color,
) -> Handler {
let supports_color = term::stderr().map_or(false, |term| term.supports_color());
- let color_cfg = if supports_color {
- ColorConfig::Auto
+ let emit_color = if supports_color {
+ ColorConfig::from(color)
} else {
ColorConfig::Never
};
@@ -128,7 +140,7 @@ fn default_handler(
false,
);
Box::new(EmitterWriter::stderr(
- color_cfg,
+ emit_color,
Some(source_map.clone()),
None,
fallback_bundle,
@@ -167,6 +179,7 @@ impl ParseSess {
Lrc::clone(&ignore_path_set),
Lrc::clone(&can_reset_errors),
config.hide_parse_errors(),
+ config.color(),
);
let parse_sess = RawParseSess::with_span_handler(handler, source_map);
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index f548388ed..18a08f17b 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -578,49 +578,41 @@ impl Rewrite for ast::GenericBounds {
impl Rewrite for ast::GenericParam {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
- let mut result = String::with_capacity(128);
// FIXME: If there are more than one attributes, this will force multiline.
- match self.attrs.rewrite(context, shape) {
- Some(ref rw) if !rw.is_empty() => {
- result.push_str(rw);
- // When rewriting generic params, an extra newline should be put
- // if the attributes end with a doc comment
- if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) {
- result.push_str(&shape.indent.to_string_with_newline(context.config));
- } else {
- result.push(' ');
- }
- }
- _ => (),
- }
+ let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new());
+ let has_attrs = !result.is_empty();
- if let ast::GenericParamKind::Const {
+ let mut param = String::with_capacity(128);
+
+ let param_start = if let ast::GenericParamKind::Const {
ref ty,
- kw_span: _,
+ kw_span,
default,
} = &self.kind
{
- result.push_str("const ");
- result.push_str(rewrite_ident(context, self.ident));
- result.push_str(": ");
- result.push_str(&ty.rewrite(context, shape)?);
+ param.push_str("const ");
+ param.push_str(rewrite_ident(context, self.ident));
+ param.push_str(": ");
+ param.push_str(&ty.rewrite(context, shape)?);
if let Some(default) = default {
let eq_str = match context.config.type_punctuation_density() {
TypeDensity::Compressed => "=",
TypeDensity::Wide => " = ",
};
- result.push_str(eq_str);
- let budget = shape.width.checked_sub(result.len())?;
+ param.push_str(eq_str);
+ let budget = shape.width.checked_sub(param.len())?;
let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?;
- result.push_str(&rewrite);
+ param.push_str(&rewrite);
}
+ kw_span.lo()
} else {
- result.push_str(rewrite_ident(context, self.ident));
- }
+ param.push_str(rewrite_ident(context, self.ident));
+ self.ident.span.lo()
+ };
if !self.bounds.is_empty() {
- result.push_str(type_bound_colon(context));
- result.push_str(&self.bounds.rewrite(context, shape)?)
+ param.push_str(type_bound_colon(context));
+ param.push_str(&self.bounds.rewrite(context, shape)?)
}
if let ast::GenericParamKind::Type {
default: Some(ref def),
@@ -630,11 +622,33 @@ impl Rewrite for ast::GenericParam {
TypeDensity::Compressed => "=",
TypeDensity::Wide => " = ",
};
- result.push_str(eq_str);
- let budget = shape.width.checked_sub(result.len())?;
+ param.push_str(eq_str);
+ let budget = shape.width.checked_sub(param.len())?;
let rewrite =
- def.rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?;
- result.push_str(&rewrite);
+ def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?;
+ param.push_str(&rewrite);
+ }
+
+ if let Some(last_attr) = self.attrs.last().filter(|last_attr| {
+ contains_comment(context.snippet(mk_sp(last_attr.span.hi(), param_start)))
+ }) {
+ result = combine_strs_with_missing_comments(
+ context,
+ &result,
+ &param,
+ mk_sp(last_attr.span.hi(), param_start),
+ shape,
+ !last_attr.is_doc_comment(),
+ )?;
+ } else {
+ // When rewriting generic params, an extra newline should be put
+ // if the attributes end with a doc comment
+ if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) {
+ result.push_str(&shape.indent.to_string_with_newline(context.config));
+ } else if has_attrs {
+ result.push(' ');
+ }
+ result.push_str(&param);
}
Some(result)
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index ca1716574..890a05b8c 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -505,6 +505,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::Range(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Become(..)
| ast::ExprKind::Yeet(..)
| ast::ExprKind::Tup(..)
| ast::ExprKind::Type(..)
diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs
index 7ff301e80..4936a7174 100644
--- a/src/tools/rustfmt/tests/rustfmt/main.rs
+++ b/src/tools/rustfmt/tests/rustfmt/main.rs
@@ -174,3 +174,15 @@ fn rustfmt_emits_error_on_line_overflow_true() {
"line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
))
}
+
+#[test]
+#[allow(non_snake_case)]
+fn dont_emit_ICE() {
+ let files = ["tests/target/issue_5728.rs", "tests/target/issue_5729.rs"];
+
+ for file in files {
+ let args = [file];
+ let (_stdout, stderr) = rustfmt(&args);
+ assert!(!stderr.contains("thread 'main' panicked"));
+ }
+}
diff --git a/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/100.rs b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/100.rs
new file mode 100644
index 000000000..a73c9084b
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/100.rs
@@ -0,0 +1,40 @@
+// rustfmt-single_line_let_else_max_width: 100
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else {
+ return
+ };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return
+ };
+
+ let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+
+ let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else {
+ return
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None)
+ };
+
+ let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#))
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None)
+ };
+
+ let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else {
+ return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"))
+ };
+}
diff --git a/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/50.rs b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/50.rs
new file mode 100644
index 000000000..87d0583c5
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/50.rs
@@ -0,0 +1,40 @@
+// rustfmt-single_line_let_else_max_width: 50
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else {
+ return
+ };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return
+ };
+
+ let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+
+ let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else {
+ return
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None)
+ };
+
+ let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#))
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None)
+ };
+
+ let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else {
+ return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"))
+ };
+}
diff --git a/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/zero.rs b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/zero.rs
new file mode 100644
index 000000000..afb9e5033
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/configs/single_line_let_else_max_width/zero.rs
@@ -0,0 +1,40 @@
+// rustfmt-single_line_let_else_max_width: 0
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else {
+ return
+ };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return
+ };
+
+ let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+
+ let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else {
+ return
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None)
+ };
+
+ let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#))
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None)
+ };
+
+ let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else {
+ return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"))
+ };
+}
diff --git a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/default.rs b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/default.rs
index 68bc40271..95238c548 100644
--- a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/default.rs
+++ b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/default.rs
@@ -23,3 +23,13 @@ fn main() {
sit
};
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+}
diff --git a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/max.rs b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/max.rs
index 8d30932e2..b79302e22 100644
--- a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/max.rs
+++ b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/max.rs
@@ -23,3 +23,13 @@ fn main() {
sit
};
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+}
diff --git a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/off.rs b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/off.rs
index f76392d24..80bcdd898 100644
--- a/src/tools/rustfmt/tests/source/configs/use_small_heuristics/off.rs
+++ b/src/tools/rustfmt/tests/source/configs/use_small_heuristics/off.rs
@@ -23,3 +23,13 @@ fn main() {
sit
};
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+}
diff --git a/src/tools/rustfmt/tests/source/issue-4041.rs b/src/tools/rustfmt/tests/source/issue-4041.rs
new file mode 100644
index 000000000..274b80f1b
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-4041.rs
@@ -0,0 +1,5 @@
+// rustfmt-wrap_comments: true
+//! List:
+//! - Sub list:
+//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah foo baar baxxxxxxxx long line 1231421230912i3091238192038
+//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah
diff --git a/src/tools/rustfmt/tests/source/issue-5234.rs b/src/tools/rustfmt/tests/source/issue-5234.rs
new file mode 100644
index 000000000..67266f485
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5234.rs
@@ -0,0 +1,51 @@
+// rustfmt-format_code_in_doc_comments: true
+
+/// ```
+/// ```
+fn foo() {}
+
+/// ```
+///Something
+/// ```
+fn foo() {}
+
+/// ```
+///
+/// ```
+fn foo() {}
+
+
+/// /// ```
+fn foo() {}
+
+/// /// ```
+/// ```
+///
+/// ```
+/// ```
+fn foo() {}
+
+fn foo() {
+/// ```
+///
+/// ```
+struct bar {}
+}
+
+/// ```
+/// fn com(){
+/// let i = 5;
+///
+/// let j = 6;
+/// }
+/// ```
+fn foo() {}
+
+fn foo() {
+/// ```
+///fn com(){
+///let i = 5;
+///}
+/// ```
+struct bar {}
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5488.rs b/src/tools/rustfmt/tests/source/issue-5488.rs
new file mode 100644
index 000000000..d361632e2
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5488.rs
@@ -0,0 +1,17 @@
+// rustfmt-use_field_init_shorthand: true
+
+struct MyStruct(u32);
+struct AnotherStruct {
+ a: u32,
+}
+
+fn main() {
+ // Since MyStruct is a tuple struct, it should not be shorthanded to
+ // MyStruct { 0 } even if use_field_init_shorthand is enabled.
+ let instance = MyStruct { 0: 0 };
+
+ // Since AnotherStruct is not a tuple struct, the shorthand should
+ // apply.
+ let a = 10;
+ let instance = AnotherStruct { a: a };
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5586.rs b/src/tools/rustfmt/tests/source/issue-5586.rs
new file mode 100644
index 000000000..9cf6c1d58
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5586.rs
@@ -0,0 +1,164 @@
+// rustfmt-version: Two
+fn main() {
+ // sample 1
+ {
+ {
+ {
+ {
+ {
+ let push_ident =
+ if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+);
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 2
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 3
+ {
+ {
+ {
+ {
+ {
+ let push_ident =
+ if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 4
+ {{{{{
+ let push_ident =
+ if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }}}}}
+
+ // sample 5
+ {
+ {
+ {
+ {
+ {
+ let push_ident =
+ if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 6
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 7
+ {
+ {
+ {
+ {
+ {
+ let push_ident =
+ if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/tools/rustfmt/tests/source/issue_5686.rs b/src/tools/rustfmt/tests/source/issue_5686.rs
new file mode 100644
index 000000000..3bf96f73b
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue_5686.rs
@@ -0,0 +1,40 @@
+#[repr(u8)]
+enum MyEnum {
+ UnitWithExplicitDiscriminant = 0,
+ EmptyStructSingleLineBlockComment {
+ /* Comment */
+ } = 1,
+ EmptyStructMultiLineBlockComment {
+ /*
+ * Comment
+ */
+ } = 2,
+ EmptyStructLineComment {
+ // comment
+ } = 3,
+ EmptyTupleSingleLineBlockComment(
+ /* Comment */
+ ) = 4,
+ EmptyTupleMultiLineBlockComment(
+ /*
+ * Comment
+ */
+ ) = 5,
+ EmptyTupleLineComment(
+ // comment
+ ) = 6,
+}
+
+enum Animal {
+ Dog(/* tuple variant closer in comment -> ) */) = 1,
+ #[hello(world)]
+ Cat(/* tuple variant close in leading attribute */) = 2,
+ Bee(/* tuple variant closer on associated field attribute */ #[hello(world)] usize) = 3,
+ Fox(/* tuple variant closer on const fn call */) = some_const_fn(),
+ Ant(/* tuple variant closer on macro call */) = some_macro!(),
+ Snake {/* stuct variant closer in comment -> } */} = 6,
+ #[hell{world}]
+ Cobra {/* struct variant close in leading attribute */} = 6,
+ Eagle {/* struct variant closer on associated field attribute */ #[hell{world}]value: Sting} = 7,
+ Koala {/* struct variant closer on macro call */} = some_macro!{}
+}
diff --git a/src/tools/rustfmt/tests/source/itemized-blocks/no_wrap.rs b/src/tools/rustfmt/tests/source/itemized-blocks/no_wrap.rs
index a7b6a10a0..e5699e766 100644
--- a/src/tools/rustfmt/tests/source/itemized-blocks/no_wrap.rs
+++ b/src/tools/rustfmt/tests/source/itemized-blocks/no_wrap.rs
@@ -1,7 +1,7 @@
// rustfmt-normalize_comments: true
// rustfmt-format_code_in_doc_comments: true
-//! This is a list:
+//! This is an itemized markdown list (see also issue #3224):
//! * Outer
//! * Outer
//! * Inner
@@ -13,6 +13,40 @@
//! - when the log level is info, the level name is green and the rest of the line is white
//! - when the log level is debug, the whole line is white
//! - when the log level is trace, the whole line is gray ("bright black")
+//!
+//! This is a numbered markdown list (see also issue #5416):
+//! 1. Long long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long long line
+//! 3. Nested list
+//! 1. Long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long line
+//! 4. Last item
+//!
+//! Using the ')' instead of '.' character after the number:
+//! 1) Long long long long long long long long long long long long long long long long long line
+//! 2) Another very long long long long long long long long long long long long long long long line
+//!
+//! Deep list that mixes various bullet and number formats:
+//! 1. First level with a long long long long long long long long long long long long long long
+//! long long long line
+//! 2. First level with another very long long long long long long long long long long long long
+//! long long long line
+//! * Second level with a long long long long long long long long long long long long long
+//! long long long line
+//! * Second level with another very long long long long long long long long long long long
+//! long long long line
+//! 1) Third level with a long long long long long long long long long long long long long
+//! long long long line
+//! 2) Third level with another very long long long long long long long long long long
+//! long long long long line
+//! - Forth level with a long long long long long long long long long long long long
+//! long long long long line
+//! - Forth level with another very long long long long long long long long long long
+//! long long long long line
+//! 3) One more item at the third level
+//! 4) Last item of the third level
+//! * Last item of second level
+//! 3. Last item of first level
/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote
/// theater, i.e., as passed to [`Theater::send`] on the remote actor:
diff --git a/src/tools/rustfmt/tests/source/itemized-blocks/wrap.rs b/src/tools/rustfmt/tests/source/itemized-blocks/wrap.rs
index 955cc698b..768461a43 100644
--- a/src/tools/rustfmt/tests/source/itemized-blocks/wrap.rs
+++ b/src/tools/rustfmt/tests/source/itemized-blocks/wrap.rs
@@ -2,7 +2,7 @@
// rustfmt-format_code_in_doc_comments: true
// rustfmt-max_width: 50
-//! This is a list:
+//! This is an itemized markdown list (see also issue #3224):
//! * Outer
//! * Outer
//! * Inner
@@ -14,6 +14,40 @@
//! - when the log level is info, the level name is green and the rest of the line is white
//! - when the log level is debug, the whole line is white
//! - when the log level is trace, the whole line is gray ("bright black")
+//!
+//! This is a numbered markdown list (see also issue #5416):
+//! 1. Long long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long long line
+//! 3. Nested list
+//! 1. Long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long line
+//! 4. Last item
+//!
+//! Using the ')' instead of '.' character after the number:
+//! 1) Long long long long long long long long long long long long long long long long long line
+//! 2) Another very long long long long long long long long long long long long long long long line
+//!
+//! Deep list that mixes various bullet and number formats:
+//! 1. First level with a long long long long long long long long long long long long long long
+//! long long long line
+//! 2. First level with another very long long long long long long long long long long long long
+//! long long long line
+//! * Second level with a long long long long long long long long long long long long long
+//! long long long line
+//! * Second level with another very long long long long long long long long long long long
+//! long long long line
+//! 1) Third level with a long long long long long long long long long long long long long
+//! long long long line
+//! 2) Third level with another very long long long long long long long long long long
+//! long long long long line
+//! - Forth level with a long long long long long long long long long long long long
+//! long long long long line
+//! - Forth level with another very long long long long long long long long long long
+//! long long long long line
+//! 3) One more item at the third level
+//! 4) Last item of the third level
+//! * Last item of second level
+//! 3. Last item of first level
// This example shows how to configure fern to output really nicely colored logs
// - when the log level is error, the whole line is red
diff --git a/src/tools/rustfmt/tests/source/let_else.rs b/src/tools/rustfmt/tests/source/let_else.rs
index a6e816fb5..85b3604ad 100644
--- a/src/tools/rustfmt/tests/source/let_else.rs
+++ b/src/tools/rustfmt/tests/source/let_else.rs
@@ -1,3 +1,162 @@
+// rustfmt-single_line_let_else_max_width: 100
+
fn main() {
- let Some(1) = Some(1) else { return };
+ // Although this won't compile it still parses so make sure we can format empty else blocks
+ let Some(x) = opt else {};
+
+ // let-else may be formatted on a single line if they are "short"
+ // and only contain a single expression
+ let Some(x) = opt else { return };
+
+ let Some(x) = opt else {
+ return
+ };
+
+ let Some(x) = opt else { return; };
+
+ let Some(x) = opt else {
+ // nope
+ return;
+ };
+
+ let Some(x) = opt else { let y = 1; return y };
+
+ let Some(x) = y.foo("abc", fairly_long_identifier, "def", "123456", "string", "cheese") else { bar() };
+
+ let Some(x) = abcdef().foo("abc", some_really_really_really_long_ident, "ident", "123456").bar().baz().qux("fffffffffffffffff") else { foo_bar() };
+}
+
+fn with_comments_around_else_keyword() {
+ let Some(x) = opt /* pre else keyword block-comment */ else { return };
+
+ let Some(x) = opt else /* post else keyword block-comment */ { return };
+
+ let Some(x) = opt /* pre else keyword block-comment */ else /* post else keyword block-comment */ { return };
+
+ let Some(x) = opt // pre else keyword line-comment
+ else { return };
+
+ let Some(x) = opt else
+ // post else keyword line-comment
+ { return };
+
+ let Some(x) = opt // pre else keyword line-comment
+ else
+ // post else keyword line-comment
+ { return };
+
+}
+
+fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The formatting is left unchanged!
+ let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_long_name___B else {return};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_long_name_____C else {some_divergent_function()};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 101 (> max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_long_name__D else { return };
+}
+
+fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else {` is 99 (< max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else {return};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else {` is 101 (> max_width)
+ // Post Formatting:
+ // The else keyword and opening brace cannot fit on the same line as the initializer expr.
+ // They are formatted on the next line.
+ let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else {return};
+}
+
+fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 99 (< max_width)
+ // Post Formatting:
+ // The else keyword and opening brace cannot fit on the same line as the initializer expr.
+ // They are formatted on the next line.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else {return};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 100 (max_width)
+ // Post Formatting:
+ // Break after the `=` and put the initializer expr on it's own line.
+ // Because the initializer expr is multi-lined the else is placed on it's own line.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name____H else {return};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 109 (> max_width)
+ // Post Formatting:
+ // Break after the `=` and put the initializer expr on it's own line.
+ // Because the initializer expr is multi-lined the else is placed on it's own line.
+ // The initializer expr has a length of 91, which when indented on the next line
+ // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be
+ // before we start running into max_width issues. I suspect this is becuase the shape is
+ // accounting for the `;` at the end of the `let-else` statement.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_really_long_name______I else {return};
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 110 (> max_width)
+ // Post Formatting:
+ // Max length issues prevent us from formatting.
+ // The initializer expr has a length of 92, which if it would be indented on the next line
+ // the `(indent)init` line has a lengh of 100 which == max_width of 100.
+ // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is
+ // because the Shape is accounting for the `;` at the end of the `let-else` statement.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return};
+}
+
+fn long_patterns() {
+ let Foo {x: Bar(..), y: FooBar(..), z: Baz(..)} = opt else {
+ return;
+ };
+
+ // with version=One we don't wrap long array patterns
+ let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else {
+ return;
+ };
+
+ let ("aaaaaaaaaaaaaaaaaaa" | "bbbbbbbbbbbbbbbbb" | "cccccccccccccccccccccccc" | "dddddddddddddddd" | "eeeeeeeeeeeeeeee") = opt else {
+ return;
+ };
+
+ let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = opt else {
+ return;
+ };
+}
+
+fn with_trailing_try_operator() {
+ // Currently the trailing ? forces the else on the next line
+ // This may be revisited in style edition 2024
+ let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index])? else { return };
+
+ // 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 };
}
diff --git a/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/100.rs b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/100.rs
new file mode 100644
index 000000000..0409124a5
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/100.rs
@@ -0,0 +1,60 @@
+// rustfmt-single_line_let_else_max_width: 100
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return;
+ };
+
+ let Some(c) = opt else {
+ /* a comment should always force the block to be multi-lined */
+ return;
+ };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+
+ let Expr::Slice(ast::ExprSlice {
+ lower,
+ upper,
+ step,
+ range: _,
+ }) = slice.as_ref()
+ else {
+ return;
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+
+ let Some(doc_attr) = variant
+ .attrs
+ .iter()
+ .find(|attr| attr.path().is_ident("doc"))
+ else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#));
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None);
+ };
+
+ let Stmt::Expr(
+ Expr::Call(ExprCall {
+ args: some_args, ..
+ }),
+ _,
+ ) = last_stmt
+ else {
+ return Err(Error::new(
+ last_stmt.span(),
+ "expected last expression to be `Some(match (..) { .. })`",
+ ));
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/50.rs b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/50.rs
new file mode 100644
index 000000000..6afc2b6f2
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/50.rs
@@ -0,0 +1,62 @@
+// rustfmt-single_line_let_else_max_width: 50
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return;
+ };
+
+ let Some(c) = opt else {
+ /* a comment should always force the block to be multi-lined */
+ return;
+ };
+
+ let Some(d) = some_very_very_very_very_long_name else {
+ return;
+ };
+
+ let Expr::Slice(ast::ExprSlice {
+ lower,
+ upper,
+ step,
+ range: _,
+ }) = slice.as_ref()
+ else {
+ return;
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+
+ let Some(doc_attr) = variant
+ .attrs
+ .iter()
+ .find(|attr| attr.path().is_ident("doc"))
+ else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#));
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None);
+ };
+
+ let Stmt::Expr(
+ Expr::Call(ExprCall {
+ args: some_args, ..
+ }),
+ _,
+ ) = last_stmt
+ else {
+ return Err(Error::new(
+ last_stmt.span(),
+ "expected last expression to be `Some(match (..) { .. })`",
+ ));
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/zero.rs b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/zero.rs
new file mode 100644
index 000000000..b5fd0b9ed
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/single_line_let_else_max_width/zero.rs
@@ -0,0 +1,66 @@
+// rustfmt-single_line_let_else_max_width: 0
+
+fn main() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else {
+ return;
+ };
+
+ let Some(c) = opt else {
+ return;
+ };
+
+ let Some(c) = opt else {
+ // a comment should always force the block to be multi-lined
+ return;
+ };
+
+ let Some(c) = opt else {
+ /* a comment should always force the block to be multi-lined */
+ return;
+ };
+
+ let Some(d) = some_very_very_very_very_long_name else {
+ return;
+ };
+
+ let Expr::Slice(ast::ExprSlice {
+ lower,
+ upper,
+ step,
+ range: _,
+ }) = slice.as_ref()
+ else {
+ return;
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+
+ let Some(doc_attr) = variant
+ .attrs
+ .iter()
+ .find(|attr| attr.path().is_ident("doc"))
+ else {
+ return Err(Error::new(variant.span(), r#"expected a doc comment"#));
+ };
+
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else {
+ return Ok(None);
+ };
+
+ let Stmt::Expr(
+ Expr::Call(ExprCall {
+ args: some_args, ..
+ }),
+ _,
+ ) = last_stmt
+ else {
+ return Err(Error::new(
+ last_stmt.span(),
+ "expected last expression to be `Some(match (..) { .. })`",
+ ));
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/default.rs b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/default.rs
index d67bd9aaf..ad4073923 100644
--- a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/default.rs
+++ b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/default.rs
@@ -24,3 +24,15 @@ fn main() {
let lorem = if ipsum { dolor } else { sit };
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(d) = some_very_very_very_very_long_name else {
+ return;
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/max.rs b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/max.rs
index 785dfbea0..fe57f853d 100644
--- a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/max.rs
+++ b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/max.rs
@@ -13,3 +13,13 @@ fn main() {
let lorem = if ipsum { dolor } else { sit };
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else { return };
+
+ let Some(c) = opt else { return };
+
+ let Some(d) = some_very_very_very_very_long_name else { return };
+}
diff --git a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/off.rs b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/off.rs
index f76392d24..b0b4e4ee4 100644
--- a/src/tools/rustfmt/tests/target/configs/use_small_heuristics/off.rs
+++ b/src/tools/rustfmt/tests/target/configs/use_small_heuristics/off.rs
@@ -23,3 +23,19 @@ fn main() {
sit
};
}
+
+fn format_let_else() {
+ let Some(a) = opt else {};
+
+ let Some(b) = opt else {
+ return;
+ };
+
+ let Some(c) = opt else {
+ return;
+ };
+
+ let Some(d) = some_very_very_very_very_long_name else {
+ return;
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/doc-of-generic-item.rs b/src/tools/rustfmt/tests/target/doc-of-generic-item.rs
index 2efc5e09a..80886e74f 100644
--- a/src/tools/rustfmt/tests/target/doc-of-generic-item.rs
+++ b/src/tools/rustfmt/tests/target/doc-of-generic-item.rs
@@ -12,3 +12,21 @@ struct Foo<
/// doc of N
const N: item,
>;
+
+// Non-doc pre-comment of Foo
+/// doc of Foo
+// Non-doc post-comment of Foo
+struct Foo<
+ // Non-doc pre-comment of 'a
+ /// doc of 'a
+ // Non-doc post-comment of 'a
+ 'a,
+ // Non-doc pre-comment of T
+ /// doc of T
+ // Non-doc post-comment of T
+ T,
+ // Non-doc pre-comment of N
+ /// doc of N
+ // Non-doc post-comment of N
+ const N: item,
+>;
diff --git a/src/tools/rustfmt/tests/target/issue-4041.rs b/src/tools/rustfmt/tests/target/issue-4041.rs
new file mode 100644
index 000000000..e9c693836
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4041.rs
@@ -0,0 +1,6 @@
+// rustfmt-wrap_comments: true
+//! List:
+//! - Sub list:
+//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah
+//! foo baar baxxxxxxxx long line 1231421230912i3091238192038
+//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah
diff --git a/src/tools/rustfmt/tests/target/issue-4210-disabled.rs b/src/tools/rustfmt/tests/target/issue-4210-disabled.rs
new file mode 100644
index 000000000..3ba87aab8
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4210-disabled.rs
@@ -0,0 +1,15 @@
+// rustfmt-wrap_comments: false
+
+/// Table that is > 80 symbols:
+///
+/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+/// |-------|-----------------------------------------------------------------------------|
+/// | val | x |
+pub struct Item;
+
+/// Table value that is > 80 symbols:
+///
+/// | table | heading
+/// |----------|-----------------------------------------------------------------------------
+/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+pub struct Item2;
diff --git a/src/tools/rustfmt/tests/target/issue-4210.rs b/src/tools/rustfmt/tests/target/issue-4210.rs
new file mode 100644
index 000000000..3fd966390
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4210.rs
@@ -0,0 +1,15 @@
+// rustfmt-wrap_comments: true
+
+/// Table that is > 80 symbols:
+///
+/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+/// |-------|-----------------------------------------------------------------------------|
+/// | val | x |
+pub struct Item;
+
+/// Table value that is > 80 symbols:
+///
+/// | table | heading
+/// |----------|-----------------------------------------------------------------------------
+/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+pub struct Item2;
diff --git a/src/tools/rustfmt/tests/target/issue-5234.rs b/src/tools/rustfmt/tests/target/issue-5234.rs
new file mode 100644
index 000000000..7ee9e46d1
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5234.rs
@@ -0,0 +1,47 @@
+// rustfmt-format_code_in_doc_comments: true
+
+/// ```
+/// ```
+fn foo() {}
+
+/// ```
+/// Something
+/// ```
+fn foo() {}
+
+/// ```
+/// ```
+fn foo() {}
+
+/// /// ```
+fn foo() {}
+
+/// /// ```
+/// ```
+/// ```
+/// ```
+fn foo() {}
+
+fn foo() {
+ /// ```
+ /// ```
+ struct bar {}
+}
+
+/// ```
+/// fn com() {
+/// let i = 5;
+///
+/// let j = 6;
+/// }
+/// ```
+fn foo() {}
+
+fn foo() {
+ /// ```
+ /// fn com() {
+ /// let i = 5;
+ /// }
+ /// ```
+ struct bar {}
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5488.rs b/src/tools/rustfmt/tests/target/issue-5488.rs
new file mode 100644
index 000000000..0cb37c56f
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5488.rs
@@ -0,0 +1,17 @@
+// rustfmt-use_field_init_shorthand: true
+
+struct MyStruct(u32);
+struct AnotherStruct {
+ a: u32,
+}
+
+fn main() {
+ // Since MyStruct is a tuple struct, it should not be shorthanded to
+ // MyStruct { 0 } even if use_field_init_shorthand is enabled.
+ let instance = MyStruct { 0: 0 };
+
+ // Since AnotherStruct is not a tuple struct, the shorthand should
+ // apply.
+ let a = 10;
+ let instance = AnotherStruct { a };
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5586.rs b/src/tools/rustfmt/tests/target/issue-5586.rs
new file mode 100644
index 000000000..7033ae975
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5586.rs
@@ -0,0 +1,177 @@
+// rustfmt-version: Two
+fn main() {
+ // sample 1
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 2
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 3
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 4
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 5
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 6
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+
+ // sample 7
+ {
+ {
+ {
+ {
+ {
+ let push_ident = if let Some(&node_id) =
+ subgraph_nodes.get(pull_to_push_idx)
+ {
+ self.node_id_as_ident(node_id, false)
+ } else {
+ // Entire subgraph is pull (except for a single send/push handoff output).
+ assert_eq!(
+ 1,
+ send_ports.len(),
+ "If entire subgraph is pull, should have only one handoff output."
+ );
+ send_ports[0].clone()
+ };
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5686.rs b/src/tools/rustfmt/tests/target/issue_5686.rs
new file mode 100644
index 000000000..993f12b53
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5686.rs
@@ -0,0 +1,42 @@
+#[repr(u8)]
+enum MyEnum {
+ UnitWithExplicitDiscriminant = 0,
+ EmptyStructSingleLineBlockComment {/* Comment */} = 1,
+ EmptyStructMultiLineBlockComment {
+ /*
+ * Comment
+ */
+ } = 2,
+ EmptyStructLineComment {
+ // comment
+ } = 3,
+ EmptyTupleSingleLineBlockComment(/* Comment */) = 4,
+ EmptyTupleMultiLineBlockComment(
+ /*
+ * Comment
+ */
+ ) = 5,
+ EmptyTupleLineComment(
+ // comment
+ ) = 6,
+}
+
+enum Animal {
+ Dog(/* tuple variant closer in comment -> ) */) = 1,
+ #[hello(world)]
+ Cat(/* tuple variant close in leading attribute */) = 2,
+ Bee(
+ /* tuple variant closer on associated field attribute */ #[hello(world)] usize,
+ ) = 3,
+ Fox(/* tuple variant closer on const fn call */) = some_const_fn(),
+ Ant(/* tuple variant closer on macro call */) = some_macro!(),
+ Snake {/* stuct variant closer in comment -> } */} = 6,
+ #[hell{world}]
+ Cobra {/* struct variant close in leading attribute */} = 6,
+ Eagle {
+ /* struct variant closer on associated field attribute */
+ #[hell{world}]
+ value: Sting,
+ } = 7,
+ Koala {/* struct variant closer on macro call */} = some_macro! {},
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5691.rs b/src/tools/rustfmt/tests/target/issue_5691.rs
new file mode 100644
index 000000000..e3aad15db
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5691.rs
@@ -0,0 +1,16 @@
+struct S<const C: usize>
+where
+ [(); { num_slots!(C) }]:, {
+ /* An asterisk-based, or a double-slash-prefixed, comment here is
+ required to trigger the fmt bug.
+
+ A single-line triple-slash-prefixed comment (with a field following it) is not enough - it will not trigger the fmt bug.
+
+ Side note: If you have a combination of two, or all three of the
+ above mentioned types of comments here, some of them disappear
+ after `cargo fmt`.
+
+ The bug gets triggered even if a field definition following the
+ (asterisk-based, or a double-slash-prefixed) comment, too.
+ */
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5728.rs b/src/tools/rustfmt/tests/target/issue_5728.rs
new file mode 100644
index 000000000..e1355416f
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5728.rs
@@ -0,0 +1,5 @@
+cfg_if::cfg_if! {
+ if #[cfg(windows)] {
+ } else if #(&cpus) {
+ } else [libc::CTL_HW, libc::HW_NCPU, 0, 0]
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5729.rs b/src/tools/rustfmt/tests/target/issue_5729.rs
new file mode 100644
index 000000000..d63c83e88
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5729.rs
@@ -0,0 +1,5 @@
+cfg_if::cfg_if! {
+ if {
+ } else if #(&cpus) {
+ } else [libc::CTL_HW, libc::HW_NCPU, 0, 0]
+}
diff --git a/src/tools/rustfmt/tests/target/itemized-blocks/no_wrap.rs b/src/tools/rustfmt/tests/target/itemized-blocks/no_wrap.rs
index de8856382..86818b447 100644
--- a/src/tools/rustfmt/tests/target/itemized-blocks/no_wrap.rs
+++ b/src/tools/rustfmt/tests/target/itemized-blocks/no_wrap.rs
@@ -1,7 +1,7 @@
// rustfmt-normalize_comments: true
// rustfmt-format_code_in_doc_comments: true
-//! This is a list:
+//! This is an itemized markdown list (see also issue #3224):
//! * Outer
//! * Outer
//! * Inner
@@ -13,6 +13,40 @@
//! - when the log level is info, the level name is green and the rest of the line is white
//! - when the log level is debug, the whole line is white
//! - when the log level is trace, the whole line is gray ("bright black")
+//!
+//! This is a numbered markdown list (see also issue #5416):
+//! 1. Long long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long long line
+//! 3. Nested list
+//! 1. Long long long long long long long long long long long long long long long long line
+//! 2. Another very long long long long long long long long long long long long long long line
+//! 4. Last item
+//!
+//! Using the ')' instead of '.' character after the number:
+//! 1) Long long long long long long long long long long long long long long long long long line
+//! 2) Another very long long long long long long long long long long long long long long long line
+//!
+//! Deep list that mixes various bullet and number formats:
+//! 1. First level with a long long long long long long long long long long long long long long
+//! long long long line
+//! 2. First level with another very long long long long long long long long long long long long
+//! long long long line
+//! * Second level with a long long long long long long long long long long long long long
+//! long long long line
+//! * Second level with another very long long long long long long long long long long long
+//! long long long line
+//! 1) Third level with a long long long long long long long long long long long long long
+//! long long long line
+//! 2) Third level with another very long long long long long long long long long long
+//! long long long long line
+//! - Forth level with a long long long long long long long long long long long long
+//! long long long long line
+//! - Forth level with another very long long long long long long long long long long
+//! long long long long line
+//! 3) One more item at the third level
+//! 4) Last item of the third level
+//! * Last item of second level
+//! 3. Last item of first level
/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote
/// theater, i.e., as passed to [`Theater::send`] on the remote actor:
diff --git a/src/tools/rustfmt/tests/target/itemized-blocks/wrap.rs b/src/tools/rustfmt/tests/target/itemized-blocks/wrap.rs
index a4907303c..4826590ea 100644
--- a/src/tools/rustfmt/tests/target/itemized-blocks/wrap.rs
+++ b/src/tools/rustfmt/tests/target/itemized-blocks/wrap.rs
@@ -2,7 +2,8 @@
// rustfmt-format_code_in_doc_comments: true
// rustfmt-max_width: 50
-//! This is a list:
+//! This is an itemized markdown list (see also
+//! issue #3224):
//! * Outer
//! * Outer
//! * Inner
@@ -23,6 +24,65 @@
//! is white
//! - when the log level is trace, the whole line
//! is gray ("bright black")
+//!
+//! This is a numbered markdown list (see also
+//! issue #5416):
+//! 1. Long long long long long long long long
+//! long long long long long long long long
+//! long line
+//! 2. Another very long long long long long long
+//! long long long long long long long long
+//! long line
+//! 3. Nested list
+//! 1. Long long long long long long long long
+//! long long long long long long long long
+//! line
+//! 2. Another very long long long long long
+//! long long long long long long long long
+//! long line
+//! 4. Last item
+//!
+//! Using the ')' instead of '.' character after
+//! the number:
+//! 1) Long long long long long long long long
+//! long long long long long long long long
+//! long line
+//! 2) Another very long long long long long long
+//! long long long long long long long long
+//! long line
+//!
+//! Deep list that mixes various bullet and number
+//! formats:
+//! 1. First level with a long long long long long
+//! long long long long long long long long
+//! long long long long line
+//! 2. First level with another very long long
+//! long long long long long long long long
+//! long long long long long line
+//! * Second level with a long long long long
+//! long long long long long long long long
+//! long long long long line
+//! * Second level with another very long long
+//! long long long long long long long long
+//! long long long long line
+//! 1) Third level with a long long long
+//! long long long long long long long
+//! long long long long long long line
+//! 2) Third level with another very long
+//! long long long long long long long
+//! long long long long long long line
+//! - Forth level with a long long
+//! long long long long long long
+//! long long long long long long
+//! long long line
+//! - Forth level with another very
+//! long long long long long long
+//! long long long long long long
+//! long long line
+//! 3) One more item at the third level
+//! 4) Last item of the third level
+//! * Last item of second level
+//! 3. Last item of first level
// This example shows how to configure fern to
// output really nicely colored logs
diff --git a/src/tools/rustfmt/tests/target/let_else.rs b/src/tools/rustfmt/tests/target/let_else.rs
index a6e816fb5..6554a0961 100644
--- a/src/tools/rustfmt/tests/target/let_else.rs
+++ b/src/tools/rustfmt/tests/target/let_else.rs
@@ -1,3 +1,254 @@
+// rustfmt-single_line_let_else_max_width: 100
+
fn main() {
- let Some(1) = Some(1) else { return };
+ // Although this won't compile it still parses so make sure we can format empty else blocks
+ let Some(x) = opt else {};
+
+ // let-else may be formatted on a single line if they are "short"
+ // and only contain a single expression
+ let Some(x) = opt else { return };
+
+ let Some(x) = opt else { return };
+
+ let Some(x) = opt else {
+ return;
+ };
+
+ let Some(x) = opt else {
+ // nope
+ return;
+ };
+
+ let Some(x) = opt else {
+ let y = 1;
+ return y;
+ };
+
+ let Some(x) = y.foo(
+ "abc",
+ fairly_long_identifier,
+ "def",
+ "123456",
+ "string",
+ "cheese",
+ ) else {
+ bar()
+ };
+
+ let Some(x) = abcdef()
+ .foo(
+ "abc",
+ some_really_really_really_long_ident,
+ "ident",
+ "123456",
+ )
+ .bar()
+ .baz()
+ .qux("fffffffffffffffff")
+ else {
+ foo_bar()
+ };
+}
+
+fn with_comments_around_else_keyword() {
+ let Some(x) = opt
+ /* pre else keyword block-comment */
+ else {
+ return;
+ };
+
+ let Some(x) = opt else
+ /* post else keyword block-comment */
+ {
+ return;
+ };
+
+ let Some(x) = opt
+ /* pre else keyword block-comment */
+ else
+ /* post else keyword block-comment */
+ {
+ return;
+ };
+
+ let Some(x) = opt
+ // pre else keyword line-comment
+ else {
+ return;
+ };
+
+ let Some(x) = opt else
+ // post else keyword line-comment
+ {
+ return;
+ };
+
+ let Some(x) = opt
+ // pre else keyword line-comment
+ else
+ // post else keyword line-comment
+ {
+ return;
+ };
+}
+
+fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The formatting is left unchanged!
+ let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_long_name___B else {
+ return;
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 100 (max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_long_name_____C else {
+ some_divergent_function()
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else block;` is 101 (> max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_long_name__D else {
+ return;
+ };
+}
+
+fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else {` is 99 (< max_width)
+ // Post Formatting:
+ // The else keyword and opening brace remain on the same line as the initializer expr,
+ // and the else block is formatted over multiple lines because we can't fit the
+ // else block on the same line as the initializer expr.
+ let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else {
+ return;
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init else {` is 101 (> max_width)
+ // Post Formatting:
+ // The else keyword and opening brace cannot fit on the same line as the initializer expr.
+ // They are formatted on the next line.
+ let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F
+ else {
+ return;
+ };
+}
+
+fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() {
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 99 (< max_width)
+ // Post Formatting:
+ // The else keyword and opening brace cannot fit on the same line as the initializer expr.
+ // They are formatted on the next line.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G
+ else {
+ return;
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 100 (max_width)
+ // Post Formatting:
+ // Break after the `=` and put the initializer expr on it's own line.
+ // Because the initializer expr is multi-lined the else is placed on it's own line.
+ let Some(x) =
+ some_really_really_really_really_really_really_really_really_really_long_name____H
+ else {
+ return;
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 109 (> max_width)
+ // Post Formatting:
+ // Break after the `=` and put the initializer expr on it's own line.
+ // Because the initializer expr is multi-lined the else is placed on it's own line.
+ // The initializer expr has a length of 91, which when indented on the next line
+ // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be
+ // before we start running into max_width issues. I suspect this is becuase the shape is
+ // accounting for the `;` at the end of the `let-else` statement.
+ let Some(x) =
+ some_really_really_really_really_really_really_really_really_really_really_long_name______I
+ else {
+ return;
+ };
+
+ // Pre Formatting:
+ // The length of `(indent)let pat = init` is 110 (> max_width)
+ // Post Formatting:
+ // Max length issues prevent us from formatting.
+ // The initializer expr has a length of 92, which if it would be indented on the next line
+ // the `(indent)init` line has a lengh of 100 which == max_width of 100.
+ // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is
+ // because the Shape is accounting for the `;` at the end of the `let-else` statement.
+ let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return};
+}
+
+fn long_patterns() {
+ let Foo {
+ x: Bar(..),
+ y: FooBar(..),
+ z: Baz(..),
+ } = opt
+ else {
+ return;
+ };
+
+ // with version=One we don't wrap long array patterns
+ let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else {
+ return;
+ };
+
+ let ("aaaaaaaaaaaaaaaaaaa"
+ | "bbbbbbbbbbbbbbbbb"
+ | "cccccccccccccccccccccccc"
+ | "dddddddddddddddd"
+ | "eeeeeeeeeeeeeeee") = opt
+ else {
+ return;
+ };
+
+ let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) =
+ opt
+ else {
+ return;
+ };
+}
+
+fn with_trailing_try_operator() {
+ // Currently the trailing ? forces the else on the next line
+ // This may be revisited in style edition 2024
+ let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket(
+ ctx,
+ logger,
+ &ranking_rule_universes[cur_ranking_rule_index],
+ )?
+ else {
+ return;
+ };
+
+ // 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;
+ };
}
diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs
index 197e9a996..64ba79dc1 100644
--- a/src/tools/tidy/src/bins.rs
+++ b/src/tools/tidy/src/bins.rs
@@ -95,17 +95,41 @@ mod os_impl {
return true;
}
+ // FIXME: check when rust-installer test sh files will be removed,
+ // and then remove them from exclude list
+ const RI_EXCLUSION_LIST: &[&str] = &[
+ "src/tools/rust-installer/test/image1/bin/program",
+ "src/tools/rust-installer/test/image1/bin/program2",
+ "src/tools/rust-installer/test/image1/bin/bad-bin",
+ "src/tools/rust-installer/test/image2/bin/oldprogram",
+ "src/tools/rust-installer/test/image3/bin/cargo",
+ ];
+
+ fn filter_rust_installer_no_so_bins(path: &Path) -> bool {
+ RI_EXCLUSION_LIST.iter().any(|p| path.ends_with(p))
+ }
+
#[cfg(unix)]
pub fn check(path: &Path, bad: &mut bool) {
use std::ffi::OsStr;
const ALLOWED: &[&str] = &["configure", "x"];
+ for p in RI_EXCLUSION_LIST {
+ if !path.join(Path::new(p)).exists() {
+ tidy_error!(bad, "rust-installer test bins missed: {p}");
+ }
+ }
+
// FIXME: we don't need to look at all binaries, only files that have been modified in this branch
// (e.g. using `git ls-files`).
walk_no_read(
&[path],
- |path, _is_dir| filter_dirs(path) || path.ends_with("src/etc"),
+ |path, _is_dir| {
+ filter_dirs(path)
+ || path.ends_with("src/etc")
+ || filter_rust_installer_no_so_bins(path)
+ },
&mut |entry| {
let file = entry.path();
let extension = file.extension();
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 2e7781109..ecc84c161 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -38,7 +38,6 @@ const LICENSES: &[&str] = &[
const EXCEPTIONS: &[(&str, &str)] = &[
// tidy-alphabetical-start
("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
- ("codespan-reporting", "Apache-2.0"), // cxx via iana-time-zone-haiku via time, only on haiku
("colored", "MPL-2.0"), // rustfmt
("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
@@ -55,7 +54,7 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
// tidy-alphabetical-start
("bitmaps", "MPL-2.0+"),
("bytesize", "Apache-2.0"),
- ("dunce", "CC0-1.0 OR MIT-0"),
+ ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
("im-rc", "MPL-2.0+"),
("imara-diff", "Apache-2.0"),
@@ -77,6 +76,7 @@ const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
+ ("cranelift-control", "Apache-2.0 WITH LLVM-exception"),
("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
("cranelift-isle", "Apache-2.0 WITH LLVM-exception"),
@@ -99,6 +99,8 @@ const EXCEPTIONS_BOOTSTRAP: &[(&str, &str)] = &[
/// these and all their dependencies *must not* be in the exception list.
const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
+const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!());
+
/// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
///
/// This list is here to provide a speed-bump to adding a new dependency to
@@ -109,8 +111,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"adler",
"ahash",
"aho-corasick",
+ "allocator-api2", // FIXME: only appears in Cargo.lock due to https://github.com/rust-lang/cargo/issues/10801
"annotate-snippets",
- "ansi_term",
"ar_archive_writer",
"arrayvec",
"atty",
@@ -120,10 +122,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"byteorder", // via ruzstd in object in thorin-dwp
"cc",
"cfg-if",
- "chalk-derive",
- "chalk-engine",
- "chalk-ir",
- "chalk-solve",
"compiler_builtins",
"convert_case", // dependency of derive_more
"cpufeatures",
@@ -143,11 +141,11 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"either",
"elsa",
"ena",
+ "equivalent",
"expect-test",
"fallible-iterator", // dependency of `thorin`
"fastrand",
"field-offset",
- "fixedbitset",
"flate2",
"fluent-bundle",
"fluent-langneg",
@@ -169,12 +167,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"instant",
"intl-memoizer",
"intl_pluralrules",
+ "io-lifetimes",
"itertools",
"itoa",
"jobserver",
"lazy_static",
"libc",
"libloading",
+ "linux-raw-sys",
"litemap",
"lock_api",
"log",
@@ -185,15 +185,16 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"memmap2",
"memoffset",
"miniz_oxide",
+ "nu-ansi-term",
"num_cpus",
"object",
"odht",
"once_cell",
+ "overload",
"parking_lot",
"parking_lot_core",
"pathdiff",
"perf-event-open-sys",
- "petgraph",
"pin-project-lite",
"polonius-engine",
"ppv-lite86",
@@ -212,12 +213,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"regex",
"regex-automata",
"regex-syntax",
- "remove_dir_all",
"rustc-demangle",
"rustc-hash",
"rustc-rayon",
"rustc-rayon-core",
"rustc_version",
+ "rustix",
"ruzstd", // via object in thorin-dwp
"ryu",
"scoped-tls",
@@ -281,6 +282,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"winapi-util",
"winapi-x86_64-pc-windows-gnu",
"windows",
+ "windows-sys",
"windows-targets",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -304,15 +306,16 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
// tidy-alphabetical-start
"ahash",
"anyhow",
+ "arbitrary",
"autocfg",
"bitflags",
"bumpalo",
- "byteorder",
"cfg-if",
"cranelift-bforest",
"cranelift-codegen",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
+ "cranelift-control",
"cranelift-entity",
"cranelift-frontend",
"cranelift-isle",
@@ -322,7 +325,6 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
"cranelift-object",
"crc32fast",
"fallible-iterator",
- "fxhash",
"gimli",
"hashbrown",
"indexmap",
@@ -332,9 +334,9 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
"mach",
"memchr",
"object",
- "once_cell",
"regalloc2",
"region",
+ "rustc-hash",
"slice-group-by",
"smallvec",
"stable_deref_trait",
@@ -345,6 +347,14 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
"windows-sys",
+ "windows-targets",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
// tidy-alphabetical-end
];
@@ -487,6 +497,7 @@ fn check_permitted_dependencies(
restricted_dependency_crates: &[&'static str],
bad: &mut bool,
) {
+ let mut has_permitted_dep_error = false;
let mut deps = HashSet::new();
for to_check in restricted_dependency_crates {
let to_check = pkg_from_name(metadata, to_check);
@@ -521,6 +532,7 @@ fn check_permitted_dependencies(
"could not find allowed package `{permitted}`\n\
Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
);
+ has_permitted_dep_error = true;
}
}
@@ -533,9 +545,14 @@ fn check_permitted_dependencies(
if dep.source.is_some() {
if !permitted_dependencies.contains(dep.name.as_str()) {
tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
+ has_permitted_dep_error = true;
}
}
}
+
+ if has_permitted_dep_error {
+ eprintln!("Go to `{PERMITTED_DEPS_LOCATION}` for the list.");
+ }
}
/// Finds a package with the given name.
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 2fd4c797b..7ad8f5c5c 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -160,10 +160,10 @@ pub fn check(
for &(name, _) in gate_untested.iter() {
println!("Expected a gate test for the feature '{name}'.");
println!(
- "Hint: create a failing test file named 'feature-gate-{}.rs'\
- \n in the 'ui' test suite, with its failures due to\
- \n missing usage of `#![feature({})]`.",
- name, name
+ "Hint: create a failing test file named 'tests/ui/feature-gates/feature-gate-{}.rs',\
+ \n with its failures due to missing usage of `#![feature({})]`.",
+ name.replace("_", "-"),
+ name
);
println!(
"Hint: If you already have such a test and don't want to rename it,\
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 1c4d96c32..e21068490 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -16,6 +16,12 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, scope, ScopedJoinHandle};
fn main() {
+ // Running Cargo will read the libstd Cargo.toml
+ // which uses the unstable `public-dependency` feature.
+ //
+ // `setenv` might not be thread safe, so run it before using multiple threads.
+ env::set_var("RUSTC_BOOTSTRAP", "1");
+
let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
let output_directory: PathBuf =
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index 2f6918510..c307bcb93 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -1,5 +1,6 @@
//! Tidy check to ensure that mir opt directories do not have stale files or dashes in file names
+use miropt_test_tools::PanicStrategy;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
@@ -24,8 +25,10 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
for file in rs_files {
for bw in [32, 64] {
- for output_file in miropt_test_tools::files_for_miropt_test(&file, bw) {
- output_files.remove(&output_file.expected_file);
+ for ps in [PanicStrategy::Unwind, PanicStrategy::Abort] {
+ for output_file in miropt_test_tools::files_for_miropt_test(&file, bw, ps) {
+ output_files.remove(&output_file.expected_file);
+ }
}
}
}
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index be3a5d3aa..55bf38110 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -10,8 +10,8 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
-const ISSUES_ENTRY_LIMIT: usize = 1920;
-const ROOT_ENTRY_LIMIT: usize = 896;
+const ISSUES_ENTRY_LIMIT: usize = 1896;
+const ROOT_ENTRY_LIMIT: usize = 870;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
index 3539943eb..185e1f320 100644
--- a/src/tools/tidy/src/walk.rs
+++ b/src/tools/tidy/src/walk.rs
@@ -4,6 +4,8 @@ use std::{ffi::OsStr, fs::File, io::Read, path::Path};
/// The default directory filter.
pub fn filter_dirs(path: &Path) -> bool {
+ // FIXME: sync submodule exclusion list with rustfmt.toml
+ // bootstrap/etc
let skip = [
"tidy-test-file",
"compiler/rustc_codegen_cranelift",
@@ -15,9 +17,7 @@ pub fn filter_dirs(path: &Path) -> bool {
"src/tools/cargo",
"src/tools/clippy",
"src/tools/miri",
- "src/tools/rls",
"src/tools/rust-analyzer",
- "src/tools/rust-installer",
"src/tools/rustfmt",
"src/doc/book",
"src/doc/edition-guide",
diff --git a/src/tools/x/Cargo.lock b/src/tools/x/Cargo.lock
index 723d6cb25..09e5c7507 100644
--- a/src/tools/x/Cargo.lock
+++ b/src/tools/x/Cargo.lock
@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "x"
-version = "0.1.0"
+version = "0.1.1"