Merge ref 'caccb4d036' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref:caccb4d036Filtered ref: 0f345ed05d559bbfb754f1403b16199366cda2e0 Upstream diff:21a19c297d...caccb4d036This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
ff7e731983
5022 changed files with 125855 additions and 75176 deletions
|
|
@ -7,13 +7,25 @@ root = true
|
|||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[!src/llvm-project]
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# some tests need trailing whitespace in output snapshots
|
||||
[tests/**]
|
||||
trim_trailing_whitespace = false
|
||||
# for actual source code files of test, we still don't want trailing whitespace
|
||||
[tests/**.{rs,js}]
|
||||
trim_trailing_whitespace = true
|
||||
# these specific source files need to have trailing whitespace.
|
||||
[tests/ui/{frontmatter/frontmatter-whitespace-3.rs,parser/shebang/shebang-space.rs}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[src/llvm-project]
|
||||
indent_style = unset
|
||||
indent_size = unset
|
||||
|
||||
[*.rs]
|
||||
max_line_length = 100
|
||||
|
||||
|
|
|
|||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
|
@ -52,7 +52,7 @@ jobs:
|
|||
run_type: ${{ steps.jobs.outputs.run_type }}
|
||||
steps:
|
||||
- name: Checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Test citool
|
||||
# Only test citool on the auto branch, to reduce latency of the calculate matrix job
|
||||
# on PR/try builds.
|
||||
|
|
@ -113,16 +113,16 @@ jobs:
|
|||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
# Free up disk space on Linux and Windows by removing preinstalled components that
|
||||
# Free up disk space on Linux by removing preinstalled components that
|
||||
# we do not need. We do this to enable some of the less resource
|
||||
# intensive jobs to run on free runners, which however also have
|
||||
# less disk space.
|
||||
- name: free up disk space
|
||||
run: src/ci/scripts/free-disk-space.sh
|
||||
run: src/ci/scripts/free-disk-space-linux.sh
|
||||
if: matrix.free_disk
|
||||
|
||||
# If we don't need to free up disk space then just report how much space we have
|
||||
|
|
@ -313,7 +313,7 @@ jobs:
|
|||
if: ${{ !cancelled() && contains(fromJSON('["auto", "try"]'), needs.calculate_matrix.outputs.run_type) }}
|
||||
steps:
|
||||
- name: checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 2
|
||||
# Calculate the exit status of the whole CI workflow.
|
||||
|
|
|
|||
4
.github/workflows/dependencies.yml
vendored
4
.github/workflows/dependencies.yml
vendored
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: install the bootstrap toolchain
|
||||
|
|
@ -101,7 +101,7 @@ jobs:
|
|||
pull-requests: write
|
||||
steps:
|
||||
- name: checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: download Cargo.lock from update job
|
||||
uses: actions/download-artifact@v4
|
||||
|
|
|
|||
2
.github/workflows/ghcr.yml
vendored
2
.github/workflows/ghcr.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
# Needed to write to the ghcr.io registry
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
|
|||
2
.github/workflows/post-merge.yml
vendored
2
.github/workflows/post-merge.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
# Make sure that we have enough commits to find the parent merge commit.
|
||||
# Since all merges should be through merge commits, fetching two commits
|
||||
|
|
|
|||
2
.mailmap
2
.mailmap
|
|
@ -255,6 +255,7 @@ Guillaume Gomez <guillaume1.gomez@gmail.com>
|
|||
Guillaume Gomez <guillaume1.gomez@gmail.com> ggomez <ggomez@ggo.ifr.lan>
|
||||
Guillaume Gomez <guillaume1.gomez@gmail.com> Guillaume Gomez <ggomez@ggo.ifr.lan>
|
||||
Guillaume Gomez <guillaume1.gomez@gmail.com> Guillaume Gomez <guillaume.gomez@huawei.com>
|
||||
gnzlbg <gonzalobg88@gmail.com> <gnzlbg@users.noreply.github.com>
|
||||
hamidreza kalbasi <hamidrezakalbasi@protonmail.com>
|
||||
Hanna Kruppe <hanna.kruppe@gmail.com> <robin.kruppe@gmail.com>
|
||||
Heather <heather@cynede.net> <Cynede@Gentoo.org>
|
||||
|
|
@ -608,6 +609,7 @@ Shohei Wada <pc@wada314.jp>
|
|||
Shotaro Yamada <sinkuu@sinkuu.xyz>
|
||||
Shotaro Yamada <sinkuu@sinkuu.xyz> <sinkuu@users.noreply.github.com>
|
||||
Shyam Sundar B <shyambaskaran@outlook.com>
|
||||
Sidney Cammeresi <sac@cheesecake.org> <sac@readyset.io>
|
||||
Simon Barber-Dueck <sbarberdueck@gmail.com> Simon BD <simon@server>
|
||||
Simon Sapin <simon@exyr.org> <simon.sapin@exyr.org>
|
||||
Simonas Kazlauskas <git@kazlauskas.me> Simonas Kazlauskas <github@kazlauskas.me>
|
||||
|
|
|
|||
861
Cargo.lock
861
Cargo.lock
File diff suppressed because it is too large
Load diff
128
RELEASES.md
128
RELEASES.md
|
|
@ -1,3 +1,129 @@
|
|||
Version 1.90.0 (2025-09-18)
|
||||
===========================
|
||||
|
||||
<a id="1.90-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Split up the `unknown_or_malformed_diagnostic_attributes` lint](https://github.com/rust-lang/rust/pull/140717). This lint has been split up into four finer-grained lints, with `unknown_or_malformed_diagnostic_attributes` now being the lint group that contains these lints:
|
||||
1. `unknown_diagnostic_attributes`: unknown to the current compiler
|
||||
2. `misplaced_diagnostic_attributes`: placed on the wrong item
|
||||
3. `malformed_diagnostic_attributes`: malformed attribute syntax or options
|
||||
4. `malformed_diagnostic_format_literals`: malformed format string literal
|
||||
- [Allow constants whose final value has references to mutable/external memory, but reject such constants as patterns](https://github.com/rust-lang/rust/pull/140942)
|
||||
- [Allow volatile access to non-Rust memory, including address 0](https://github.com/rust-lang/rust/pull/141260)
|
||||
|
||||
|
||||
<a id="1.90-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525).
|
||||
- [Tier 3 `musl` targets now link dynamically by default](https://github.com/rust-lang/rust/pull/144410). Affected targets:
|
||||
- `mips64-unknown-linux-muslabi64`
|
||||
- `powerpc64-unknown-linux-musl`
|
||||
- `powerpc-unknown-linux-musl`
|
||||
- `powerpc-unknown-linux-muslspe`
|
||||
- `riscv32gc-unknown-linux-musl`
|
||||
- `s390x-unknown-linux-musl`
|
||||
- `thumbv7neon-unknown-linux-musleabihf`
|
||||
|
||||
|
||||
<a id="1.90-Platform-Support"></a>
|
||||
|
||||
Platform Support
|
||||
----------------
|
||||
- [Demote `x86_64-apple-darwin` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/145252)
|
||||
|
||||
|
||||
Refer to Rust's [platform support page][platform-support-doc]
|
||||
for more information on Rust's tiered platform support.
|
||||
|
||||
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
|
||||
|
||||
<a id="1.90-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Stabilize `u*::{checked,overflowing,saturating,wrapping}_sub_signed`](https://github.com/rust-lang/rust/issues/126043)
|
||||
- [Allow comparisons between `CStr`, `CString`, and `Cow<CStr>`](https://github.com/rust-lang/rust/pull/137268)
|
||||
- [Remove some unsized tuple impls since unsized tuples can't be constructed](https://github.com/rust-lang/rust/pull/138340)
|
||||
- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005)
|
||||
- [`proc_macro::Ident::new` now supports `$crate`.](https://github.com/rust-lang/rust/pull/141996)
|
||||
- [Guarantee the pointer returned from `Thread::into_raw` has at least 8 bytes of alignment](https://github.com/rust-lang/rust/pull/143859)
|
||||
|
||||
|
||||
<a id="1.90-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`u{n}::checked_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.checked_sub_signed)
|
||||
- [`u{n}::overflowing_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.overflowing_sub_signed)
|
||||
- [`u{n}::saturating_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.saturating_sub_signed)
|
||||
- [`u{n}::wrapping_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.wrapping_sub_signed)
|
||||
- [`impl Copy for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Copy-for-IntErrorKind)
|
||||
- [`impl Hash for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Hash-for-IntErrorKind)
|
||||
- [`impl PartialEq<&CStr> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3C%26CStr%3E-for-CStr)
|
||||
- [`impl PartialEq<CString> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCString%3E-for-CStr)
|
||||
- [`impl PartialEq<Cow<CStr>> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CStr)
|
||||
- [`impl PartialEq<&CStr> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3C%26CStr%3E-for-CString)
|
||||
- [`impl PartialEq<CStr> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCStr%3E-for-CString)
|
||||
- [`impl PartialEq<Cow<CStr>> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CString)
|
||||
- [`impl PartialEq<&CStr> for Cow<CStr>`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3C%26CStr%3E-for-Cow%3C'_,+CStr%3E)
|
||||
- [`impl PartialEq<CStr> for Cow<CStr>`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCStr%3E-for-Cow%3C'_,+CStr%3E)
|
||||
- [`impl PartialEq<CString> for Cow<CStr>`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCString%3E-for-Cow%3C'_,+CStr%3E)
|
||||
|
||||
|
||||
These previously stable APIs are now stable in const contexts:
|
||||
|
||||
- [`<[T]>::reverse`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.reverse)
|
||||
- [`f32::floor`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.floor)
|
||||
- [`f32::ceil`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.ceil)
|
||||
- [`f32::trunc`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.trunc)
|
||||
- [`f32::fract`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.fract)
|
||||
- [`f32::round`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round)
|
||||
- [`f32::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round_ties_even)
|
||||
- [`f64::floor`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.floor)
|
||||
- [`f64::ceil`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.ceil)
|
||||
- [`f64::trunc`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.trunc)
|
||||
- [`f64::fract`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.fract)
|
||||
- [`f64::round`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round)
|
||||
- [`f64::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round_ties_even)
|
||||
|
||||
|
||||
<a id="1.90-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Add `http.proxy-cainfo` config for proxy certs](https://github.com/rust-lang/cargo/pull/15374/)
|
||||
- [Use `gix` for `cargo package`](https://github.com/rust-lang/cargo/pull/15534/)
|
||||
- [feat(publish): Stabilize multi-package publishing](https://github.com/rust-lang/cargo/pull/15636/)
|
||||
|
||||
<a id="1.90-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-----
|
||||
- [Add ways to collapse all impl blocks](https://github.com/rust-lang/rust/pull/141663). Previously the "Summary" button and "-" keyboard shortcut would never collapse `impl` blocks, now they do when shift is held
|
||||
- [Display unsafe attributes with `unsafe()` wrappers](https://github.com/rust-lang/rust/pull/143662)
|
||||
|
||||
|
||||
<a id="1.90-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525).
|
||||
See also <https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/>.
|
||||
- [Make `core::iter::Fuse`'s `Default` impl construct `I::default()` internally as promised in the docs instead of always being empty](https://github.com/rust-lang/rust/pull/140985)
|
||||
- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005)
|
||||
This may change program behavior but results in the same behavior as other primitives (e.g., stdout, network sockets).
|
||||
Programs relying on signals to terminate them should update handling of sockets to handle errors on write by exiting.
|
||||
- [On Unix `std::env::home_dir` will use the fallback if the `HOME` environment variable is empty](https://github.com/rust-lang/rust/pull/141840)
|
||||
- We now [reject unsupported `extern "{abi}"`s consistently in all positions](https://github.com/rust-lang/rust/pull/142134). This primarily affects the use of implementing traits on an `extern "{abi}"` function pointer, like `extern "stdcall" fn()`, on a platform that doesn't support that, like aarch64-unknown-linux-gnu. Direct usage of these unsupported ABI strings by declaring or defining functions was already rejected, so this is only a change for consistency.
|
||||
- [const-eval: error when initializing a static writes to that static](https://github.com/rust-lang/rust/pull/143084)
|
||||
- [Check that the `proc_macro_derive` macro has correct arguments when applied to the crate root](https://github.com/rust-lang/rust/pull/143607)
|
||||
|
||||
|
||||
Version 1.89.0 (2025-08-07)
|
||||
==========================
|
||||
|
||||
|
|
@ -1778,7 +1904,7 @@ Language
|
|||
- [Undeprecate lint `unstable_features` and make use of it in the compiler.](https://github.com/rust-lang/rust/pull/118639/)
|
||||
- [Make inductive cycles in coherence ambiguous always.](https://github.com/rust-lang/rust/pull/118649/)
|
||||
- [Get rid of type-driven traversal in const-eval interning](https://github.com/rust-lang/rust/pull/119044/),
|
||||
only as a [future compatiblity lint](https://github.com/rust-lang/rust/pull/122204) for now.
|
||||
only as a [future compatibility lint](https://github.com/rust-lang/rust/pull/122204) for now.
|
||||
- [Deny braced macro invocations in let-else.](https://github.com/rust-lang/rust/pull/119062/)
|
||||
|
||||
<a id="1.77.0-Compiler"></a>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
# a custom configuration file can also be specified with `--config` to the build
|
||||
# system.
|
||||
#
|
||||
# Note that the following are equivelent, for more details see <https://toml.io/en/v1.0.0>.
|
||||
# Note that the following are equivalent, for more details see <https://toml.io/en/v1.0.0>.
|
||||
#
|
||||
# build.verbose = 1
|
||||
#
|
||||
|
|
@ -325,6 +325,9 @@
|
|||
# Defaults to the Python interpreter used to execute x.py.
|
||||
#build.python = "python"
|
||||
|
||||
# The path to (or name of) the resource compiler executable to use on Windows.
|
||||
#build.windows-rc = "rc.exe"
|
||||
|
||||
# The path to the REUSE executable to use. Note that REUSE is not required in
|
||||
# most cases, as our tooling relies on a cached (and shrunk) copy of the
|
||||
# REUSE output present in the git repository and in our source tarballs.
|
||||
|
|
@ -345,9 +348,9 @@
|
|||
# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code.
|
||||
#build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }
|
||||
|
||||
# Typically the build system will build the Rust compiler twice. The second
|
||||
# compiler, however, will simply use its own libraries to link against. If you
|
||||
# would rather to perform a full bootstrap, compiling the compiler three times,
|
||||
# If you build the compiler more than twice (stage3+) or the standard library more than once
|
||||
# (stage 2+), the third compiler and second library will get uplifted from stage2 and stage1,
|
||||
# respectively. If you would like to disable this uplifting, and rather perform a full bootstrap,
|
||||
# then you can set this option to true.
|
||||
#
|
||||
# This is only useful for verifying that rustc generates reproducible builds.
|
||||
|
|
@ -407,8 +410,11 @@
|
|||
#build.profiler = false
|
||||
|
||||
# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics.
|
||||
# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
|
||||
# sources are available.
|
||||
# Choosing true requires the LLVM submodule to be managed by bootstrap (i.e. not external)
|
||||
# so that `compiler-rt` sources are available.
|
||||
#
|
||||
# Setting this to a path removes the requirement for a C toolchain, but requires setting the
|
||||
# path to an existing library containing the builtins library from LLVM's compiler-rt.
|
||||
#
|
||||
# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
|
||||
# order to run `x check`.
|
||||
|
|
@ -482,7 +488,7 @@
|
|||
# Use `--extra-checks=''` to temporarily disable all extra checks.
|
||||
#
|
||||
# Automatically enabled in the "tools" profile.
|
||||
# Set to the empty string to force disable (recommeded for hdd systems).
|
||||
# Set to the empty string to force disable (recommended for hdd systems).
|
||||
#build.tidy-extra-checks = ""
|
||||
|
||||
# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
|
||||
|
|
@ -853,6 +859,17 @@
|
|||
# as libstd features, this option can also be used to configure features such as optimize_for_size.
|
||||
#rust.std-features = ["panic_unwind"]
|
||||
|
||||
# Trigger a `DebugBreak` after an internal compiler error during bootstrap on Windows
|
||||
#rust.break-on-ice = true
|
||||
|
||||
# Set the number of threads for the compiler frontend used during compilation of Rust code (passed to `-Zthreads`).
|
||||
# The valid options are:
|
||||
# 0 - Set the number of threads according to the detected number of threads of the host system
|
||||
# 1 - Use a single thread for compilation of Rust code (the default)
|
||||
# N - Number of threads used for compilation of Rust code
|
||||
#
|
||||
#rust.parallel-frontend-threads = 1
|
||||
|
||||
# =============================================================================
|
||||
# Distribution options
|
||||
#
|
||||
|
|
@ -1041,13 +1058,15 @@
|
|||
#runner = <none> (string)
|
||||
|
||||
# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics
|
||||
# on this target.
|
||||
# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
|
||||
# sources are available.
|
||||
# on this target. Choosing true requires the LLVM submodule to be managed by bootstrap
|
||||
# (i.e. not external) so that `compiler-rt` sources are available.
|
||||
#
|
||||
# Setting this to a path removes the requirement for a C toolchain, but requires setting the
|
||||
# path to an existing library containing the builtins library from LLVM's compiler-rt.
|
||||
#
|
||||
# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
|
||||
# order to run `x check`.
|
||||
#optimized-compiler-builtins = build.optimized-compiler-builtins (bool)
|
||||
#optimized-compiler-builtins = build.optimized-compiler-builtins (bool or path)
|
||||
|
||||
# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
|
||||
# This overrides the global `rust.jemalloc` option. See that option for more info.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ features = ['unprefixed_malloc_on_supported_platforms']
|
|||
check_only = ['rustc_driver_impl/check_only']
|
||||
jemalloc = ['dep:tikv-jemalloc-sys']
|
||||
llvm = ['rustc_driver_impl/llvm']
|
||||
llvm_enzyme = ['rustc_driver_impl/llvm_enzyme']
|
||||
max_level_info = ['rustc_driver_impl/max_level_info']
|
||||
rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[build-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_windows_rc = { path = "../rustc_windows_rc" }
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use std::env;
|
||||
use std::{env, path};
|
||||
|
||||
use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file};
|
||||
|
||||
fn main() {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS");
|
||||
|
|
@ -13,6 +15,18 @@ fn main() {
|
|||
|
||||
// Add a manifest file to rustc.exe.
|
||||
fn set_windows_exe_options() {
|
||||
set_windows_resource();
|
||||
set_windows_manifest();
|
||||
}
|
||||
|
||||
fn set_windows_resource() {
|
||||
let stem = path::PathBuf::from("rustc_main_resource");
|
||||
let file_description = "rustc";
|
||||
let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::App);
|
||||
println!("cargo:rustc-link-arg={}", res_file.display());
|
||||
}
|
||||
|
||||
fn set_windows_manifest() {
|
||||
static WINDOWS_MANIFEST_FILE: &str = "Windows Manifest.xml";
|
||||
|
||||
let mut manifest = env::current_dir().unwrap();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ bitflags = "2.4.1"
|
|||
rand = { version = "0.9.0", default-features = false, optional = true }
|
||||
rand_xoshiro = { version = "0.7.0", optional = true }
|
||||
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
|
||||
rustc_error_messages = { path = "../rustc_error_messages", optional = true }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
rustc_index = { path = "../rustc_index", default-features = false }
|
||||
rustc_macros = { path = "../rustc_macros", optional = true }
|
||||
|
|
@ -24,6 +25,7 @@ default = ["nightly", "randomize"]
|
|||
# without depending on rustc_data_structures, rustc_macros and rustc_serialize
|
||||
nightly = [
|
||||
"dep:rustc_data_structures",
|
||||
"dep:rustc_error_messages",
|
||||
"dep:rustc_macros",
|
||||
"dep:rustc_serialize",
|
||||
"dep:rustc_span",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ use std::hash::{Hash, Hasher};
|
|||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_span::Symbol;
|
||||
|
||||
use crate::AbiFromStrErr;
|
||||
|
||||
|
|
@ -223,6 +225,16 @@ impl StableOrd for ExternAbi {
|
|||
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
pub enum CVariadicStatus {
|
||||
NotSupported,
|
||||
Stable,
|
||||
Unstable { feature: Symbol },
|
||||
}
|
||||
|
||||
impl ExternAbi {
|
||||
/// An ABI "like Rust"
|
||||
///
|
||||
|
|
@ -235,23 +247,33 @@ impl ExternAbi {
|
|||
matches!(self, Rust | RustCall | RustCold)
|
||||
}
|
||||
|
||||
pub fn supports_varargs(self) -> bool {
|
||||
/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
|
||||
/// of such functions via `extern` blocks; there's a separate check during AST construction
|
||||
/// guarding *definitions* of variadic functions.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub fn supports_c_variadic(self) -> CVariadicStatus {
|
||||
// * C and Cdecl obviously support varargs.
|
||||
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
|
||||
// * EfiApi is based on Win64 or C, so it also supports it.
|
||||
// * System automatically falls back to C when used with variadics, therefore supports it.
|
||||
//
|
||||
// * Stdcall does not, because it would be impossible for the callee to clean
|
||||
// up the arguments. (callee doesn't know how many arguments are there)
|
||||
// * Same for Fastcall, Vectorcall and Thiscall.
|
||||
// * Other calling conventions are related to hardware or the compiler itself.
|
||||
//
|
||||
// All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
|
||||
match self {
|
||||
Self::C { .. }
|
||||
| Self::Cdecl { .. }
|
||||
| Self::Aapcs { .. }
|
||||
| Self::Win64 { .. }
|
||||
| Self::SysV64 { .. }
|
||||
| Self::EfiApi => true,
|
||||
_ => false,
|
||||
| Self::EfiApi => CVariadicStatus::Stable,
|
||||
Self::System { .. } => {
|
||||
CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs }
|
||||
}
|
||||
_ => CVariadicStatus::NotSupported,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -594,23 +594,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
|
||||
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
// Until we've decided whether to use the tagged or
|
||||
// niche filling LayoutData, we don't want to intern the
|
||||
// variant layouts, so we can't store them in the
|
||||
// overall LayoutData. Store the overall LayoutData
|
||||
// and the variant LayoutDatas here until then.
|
||||
struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
layout: LayoutData<FieldIdx, VariantIdx>,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
}
|
||||
|
||||
let dl = self.cx.data_layout();
|
||||
// bail if the enum has an incoherent repr that cannot be computed
|
||||
if repr.packed() {
|
||||
return Err(LayoutCalculatorError::ReprConflict);
|
||||
}
|
||||
|
||||
let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
|
||||
let calculate_niche_filling_layout = || -> Option<LayoutData<FieldIdx, VariantIdx>> {
|
||||
if repr.inhibit_enum_layout_opt() {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -746,7 +736,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
niche_start,
|
||||
},
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
variants: variant_layouts,
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [niche_offset].into(),
|
||||
|
|
@ -762,7 +752,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
randomization_seed: combined_seed,
|
||||
};
|
||||
|
||||
Some(TmpLayout { layout, variants: variant_layouts })
|
||||
Some(layout)
|
||||
};
|
||||
|
||||
let niche_filling_layout = calculate_niche_filling_layout();
|
||||
|
|
@ -1093,7 +1083,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
tag,
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
variants: layout_variants,
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [Size::ZERO].into(),
|
||||
|
|
@ -1109,18 +1099,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
randomization_seed: combined_seed,
|
||||
};
|
||||
|
||||
let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
|
||||
|
||||
let mut best_layout = match (tagged_layout, niche_filling_layout) {
|
||||
let best_layout = match (tagged_layout, niche_filling_layout) {
|
||||
(tl, Some(nl)) => {
|
||||
// Pick the smaller layout; otherwise,
|
||||
// pick the layout with the larger niche; otherwise,
|
||||
// pick tagged as it has simpler codegen.
|
||||
use cmp::Ordering::*;
|
||||
let niche_size = |tmp_l: &TmpLayout<FieldIdx, VariantIdx>| {
|
||||
tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl))
|
||||
let niche_size = |l: &LayoutData<FieldIdx, VariantIdx>| {
|
||||
l.largest_niche.map_or(0, |n| n.available(dl))
|
||||
};
|
||||
match (tl.layout.size.cmp(&nl.layout.size), niche_size(&tl).cmp(&niche_size(&nl))) {
|
||||
match (tl.size.cmp(&nl.size), niche_size(&tl).cmp(&niche_size(&nl))) {
|
||||
(Greater, _) => nl,
|
||||
(Equal, Less) => nl,
|
||||
_ => tl,
|
||||
|
|
@ -1129,16 +1117,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
(tl, None) => tl,
|
||||
};
|
||||
|
||||
// Now we can intern the variant layouts and store them in the enum layout.
|
||||
best_layout.layout.variants = match best_layout.layout.variants {
|
||||
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
||||
Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
|
||||
}
|
||||
Variants::Single { .. } | Variants::Empty => {
|
||||
panic!("encountered a single-variant or empty enum during multi-variant layout")
|
||||
}
|
||||
};
|
||||
Ok(best_layout.layout)
|
||||
Ok(best_layout)
|
||||
}
|
||||
|
||||
fn univariant_biased<
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ mod tests;
|
|||
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use extern_abi::CVariadicStatus;
|
||||
pub use extern_abi::{ExternAbi, all_names};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
|
|
@ -315,7 +317,7 @@ pub enum TargetDataLayoutErrors<'a> {
|
|||
MissingAlignment { cause: &'a str },
|
||||
InvalidAlignment { cause: &'a str, err: AlignFromBytesError },
|
||||
InconsistentTargetArchitecture { dl: &'a str, target: &'a str },
|
||||
InconsistentTargetPointerWidth { pointer_size: u64, target: u32 },
|
||||
InconsistentTargetPointerWidth { pointer_size: u64, target: u16 },
|
||||
InvalidBitsSize { err: String },
|
||||
UnknownPointerSpecification { err: String },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -937,7 +937,7 @@ pub enum PatKind {
|
|||
#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
|
||||
pub enum PatFieldsRest {
|
||||
/// `module::StructName { field, ..}`
|
||||
Rest,
|
||||
Rest(Span),
|
||||
/// `module::StructName { field, syntax error }`
|
||||
Recovered(ErrorGuaranteed),
|
||||
/// `module::StructName { field }`
|
||||
|
|
@ -2284,6 +2284,54 @@ pub struct FnSig {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
impl FnSig {
|
||||
/// Return a span encompassing the header, or where to insert it if empty.
|
||||
pub fn header_span(&self) -> Span {
|
||||
match self.header.ext {
|
||||
Extern::Implicit(span) | Extern::Explicit(_, span) => {
|
||||
return self.span.with_hi(span.hi());
|
||||
}
|
||||
Extern::None => {}
|
||||
}
|
||||
|
||||
match self.header.safety {
|
||||
Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()),
|
||||
Safety::Default => {}
|
||||
};
|
||||
|
||||
if let Some(coroutine_kind) = self.header.coroutine_kind {
|
||||
return self.span.with_hi(coroutine_kind.span().hi());
|
||||
}
|
||||
|
||||
if let Const::Yes(span) = self.header.constness {
|
||||
return self.span.with_hi(span.hi());
|
||||
}
|
||||
|
||||
self.span.shrink_to_lo()
|
||||
}
|
||||
|
||||
/// The span of the header's safety, or where to insert it if empty.
|
||||
pub fn safety_span(&self) -> Span {
|
||||
match self.header.safety {
|
||||
Safety::Unsafe(span) | Safety::Safe(span) => span,
|
||||
Safety::Default => {
|
||||
// Insert after the `coroutine_kind` if available.
|
||||
if let Some(extern_span) = self.header.ext.span() {
|
||||
return extern_span.shrink_to_lo();
|
||||
}
|
||||
|
||||
// Insert right at the front of the signature.
|
||||
self.header_span().shrink_to_hi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The span of the header's extern, or where to insert it if empty.
|
||||
pub fn extern_span(&self) -> Span {
|
||||
self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi())
|
||||
}
|
||||
}
|
||||
|
||||
/// A constraint on an associated item.
|
||||
///
|
||||
/// ### Examples
|
||||
|
|
@ -3137,7 +3185,7 @@ impl FnRetTy {
|
|||
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
|
||||
pub enum Inline {
|
||||
Yes,
|
||||
No,
|
||||
No { had_parse_error: Result<(), ErrorGuaranteed> },
|
||||
}
|
||||
|
||||
/// Module item kind.
|
||||
|
|
@ -3147,7 +3195,7 @@ pub enum ModKind {
|
|||
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
|
||||
/// The inner span is from the first token past `{` to the last token until `}`,
|
||||
/// or from the first to the last token in the loaded file.
|
||||
Loaded(ThinVec<Box<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
|
||||
Loaded(ThinVec<Box<Item>>, Inline, ModSpans),
|
||||
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
|
||||
Unloaded,
|
||||
}
|
||||
|
|
@ -3526,6 +3574,13 @@ impl Extern {
|
|||
None => Extern::Implicit(span),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(self) -> Option<Span> {
|
||||
match self {
|
||||
Extern::None => None,
|
||||
Extern::Implicit(span) | Extern::Explicit(_, span) => Some(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A function header.
|
||||
|
|
@ -3534,12 +3589,12 @@ impl Extern {
|
|||
/// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
|
||||
#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct FnHeader {
|
||||
/// Whether this is `unsafe`, or has a default safety.
|
||||
pub safety: Safety,
|
||||
/// Whether this is `async`, `gen`, or nothing.
|
||||
pub coroutine_kind: Option<CoroutineKind>,
|
||||
/// The `const` keyword, if any
|
||||
pub constness: Const,
|
||||
/// Whether this is `async`, `gen`, or nothing.
|
||||
pub coroutine_kind: Option<CoroutineKind>,
|
||||
/// Whether this is `unsafe`, or has a default safety.
|
||||
pub safety: Safety,
|
||||
/// The `extern` keyword and corresponding ABI string, if any.
|
||||
pub ext: Extern,
|
||||
}
|
||||
|
|
@ -3553,38 +3608,6 @@ impl FnHeader {
|
|||
|| matches!(constness, Const::Yes(_))
|
||||
|| !matches!(ext, Extern::None)
|
||||
}
|
||||
|
||||
/// Return a span encompassing the header, or none if all options are default.
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
fn append(a: &mut Option<Span>, b: Span) {
|
||||
*a = match a {
|
||||
None => Some(b),
|
||||
Some(x) => Some(x.to(b)),
|
||||
}
|
||||
}
|
||||
|
||||
let mut full_span = None;
|
||||
|
||||
match self.safety {
|
||||
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
|
||||
Safety::Default => {}
|
||||
};
|
||||
|
||||
if let Some(coroutine_kind) = self.coroutine_kind {
|
||||
append(&mut full_span, coroutine_kind.span());
|
||||
}
|
||||
|
||||
if let Const::Yes(span) = self.constness {
|
||||
append(&mut full_span, span);
|
||||
}
|
||||
|
||||
match self.ext {
|
||||
Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
|
||||
Extern::None => {}
|
||||
}
|
||||
|
||||
full_span
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FnHeader {
|
||||
|
|
@ -3661,17 +3684,21 @@ pub struct TyAlias {
|
|||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct Impl {
|
||||
pub defaultness: Defaultness,
|
||||
pub safety: Safety,
|
||||
pub generics: Generics,
|
||||
pub constness: Const,
|
||||
pub polarity: ImplPolarity,
|
||||
/// The trait being implemented, if any.
|
||||
pub of_trait: Option<TraitRef>,
|
||||
pub of_trait: Option<Box<TraitImplHeader>>,
|
||||
pub self_ty: Box<Ty>,
|
||||
pub items: ThinVec<Box<AssocItem>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct TraitImplHeader {
|
||||
pub defaultness: Defaultness,
|
||||
pub safety: Safety,
|
||||
pub constness: Const,
|
||||
pub polarity: ImplPolarity,
|
||||
pub trait_ref: TraitRef,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
|
||||
pub struct FnContract {
|
||||
pub requires: Option<Box<Expr>>,
|
||||
|
|
@ -3793,7 +3820,7 @@ pub enum ItemKind {
|
|||
/// An implementation.
|
||||
///
|
||||
/// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
|
||||
Impl(Box<Impl>),
|
||||
Impl(Impl),
|
||||
/// A macro invocation.
|
||||
///
|
||||
/// E.g., `foo!(..)`.
|
||||
|
|
@ -3880,7 +3907,7 @@ impl ItemKind {
|
|||
| Self::Union(_, generics, _)
|
||||
| Self::Trait(box Trait { generics, .. })
|
||||
| Self::TraitAlias(_, generics, _)
|
||||
| Self::Impl(box Impl { generics, .. }) => Some(generics),
|
||||
| Self::Impl(Impl { generics, .. }) => Some(generics),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -4040,19 +4067,20 @@ mod size_asserts {
|
|||
static_assert_size!(GenericArg, 24);
|
||||
static_assert_size!(GenericBound, 88);
|
||||
static_assert_size!(Generics, 40);
|
||||
static_assert_size!(Impl, 136);
|
||||
static_assert_size!(Impl, 64);
|
||||
static_assert_size!(Item, 144);
|
||||
static_assert_size!(ItemKind, 80);
|
||||
static_assert_size!(LitKind, 24);
|
||||
static_assert_size!(Local, 96);
|
||||
static_assert_size!(MetaItemLit, 40);
|
||||
static_assert_size!(Param, 40);
|
||||
static_assert_size!(Pat, 72);
|
||||
static_assert_size!(PatKind, 48);
|
||||
static_assert_size!(Pat, 80);
|
||||
static_assert_size!(PatKind, 56);
|
||||
static_assert_size!(Path, 24);
|
||||
static_assert_size!(PathSegment, 24);
|
||||
static_assert_size!(Stmt, 32);
|
||||
static_assert_size!(StmtKind, 16);
|
||||
static_assert_size!(TraitImplHeader, 80);
|
||||
static_assert_size!(Ty, 64);
|
||||
static_assert_size!(TyKind, 40);
|
||||
// tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pub use NtPatKind::*;
|
|||
pub use TokenKind::*;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::IdentPrintMode;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
|
||||
#[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
|
||||
#[allow(hidden_glob_reexports)]
|
||||
|
|
@ -21,8 +22,7 @@ pub enum CommentKind {
|
|||
Block,
|
||||
}
|
||||
|
||||
// This type must not implement `Hash` due to the unusual `PartialEq` impl below.
|
||||
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum InvisibleOrigin {
|
||||
// From the expansion of a metavariable in a declarative macro.
|
||||
MetaVar(MetaVarKind),
|
||||
|
|
@ -44,20 +44,6 @@ impl InvisibleOrigin {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InvisibleOrigin {
|
||||
#[inline]
|
||||
fn eq(&self, _other: &InvisibleOrigin) -> bool {
|
||||
// When we had AST-based nonterminals we couldn't compare them, and the
|
||||
// old `Nonterminal` type had an `eq` that always returned false,
|
||||
// resulting in this restriction:
|
||||
// https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment
|
||||
// This `eq` emulates that behaviour. We could consider lifting this
|
||||
// restriction now but there are still cases involving invisible
|
||||
// delimiters that make it harder than it first appears.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Annoyingly similar to `NonterminalKind`, but the slight differences are important.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
|
||||
pub enum MetaVarKind {
|
||||
|
|
@ -141,7 +127,8 @@ impl Delimiter {
|
|||
}
|
||||
}
|
||||
|
||||
// This exists because `InvisibleOrigin`s should be compared. It is only used for assertions.
|
||||
// This exists because `InvisibleOrigin`s should not be compared. It is only used for
|
||||
// assertions.
|
||||
pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool {
|
||||
match (self, other) {
|
||||
(Delimiter::Parenthesis, Delimiter::Parenthesis) => true,
|
||||
|
|
@ -344,15 +331,24 @@ pub enum IdentIsRaw {
|
|||
Yes,
|
||||
}
|
||||
|
||||
impl From<bool> for IdentIsRaw {
|
||||
fn from(b: bool) -> Self {
|
||||
if b { Self::Yes } else { Self::No }
|
||||
impl IdentIsRaw {
|
||||
pub fn to_print_mode_ident(self) -> IdentPrintMode {
|
||||
match self {
|
||||
IdentIsRaw::No => IdentPrintMode::Normal,
|
||||
IdentIsRaw::Yes => IdentPrintMode::RawIdent,
|
||||
}
|
||||
}
|
||||
pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
|
||||
match self {
|
||||
IdentIsRaw::No => IdentPrintMode::Normal,
|
||||
IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdentIsRaw> for bool {
|
||||
fn from(is_raw: IdentIsRaw) -> bool {
|
||||
matches!(is_raw, IdentIsRaw::Yes)
|
||||
impl From<bool> for IdentIsRaw {
|
||||
fn from(b: bool) -> Self {
|
||||
if b { Self::Yes } else { Self::No }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,15 +3,6 @@
|
|||
//! `TokenStream`s represent syntactic objects before they are converted into ASTs.
|
||||
//! A `TokenStream` is, roughly speaking, a sequence of [`TokenTree`]s,
|
||||
//! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens.
|
||||
//!
|
||||
//! ## Ownership
|
||||
//!
|
||||
//! `TokenStream`s are persistent data structures constructed as ropes with reference
|
||||
//! counted-children. In general, this means that calling an operation on a `TokenStream`
|
||||
//! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to
|
||||
//! the original. This essentially coerces `TokenStream`s into "views" of their subparts,
|
||||
//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking
|
||||
//! ownership of the original.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
|
|
@ -99,17 +90,6 @@ impl TokenTree {
|
|||
}
|
||||
}
|
||||
|
||||
impl<CTX> HashStable<CTX> for TokenStream
|
||||
where
|
||||
CTX: crate::HashStableContext,
|
||||
{
|
||||
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||
for sub_tt in self.iter() {
|
||||
sub_tt.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy version of [`AttrTokenStream`], which defers creation of an actual
|
||||
/// `AttrTokenStream` until it is needed.
|
||||
#[derive(Clone)]
|
||||
|
|
@ -556,10 +536,6 @@ pub struct AttrsTarget {
|
|||
pub tokens: LazyAttrTokenStream,
|
||||
}
|
||||
|
||||
/// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s.
|
||||
#[derive(Clone, Debug, Default, Encodable, Decodable)]
|
||||
pub struct TokenStream(pub(crate) Arc<Vec<TokenTree>>);
|
||||
|
||||
/// Indicates whether a token can join with the following token to form a
|
||||
/// compound token. Used for conversions to `proc_macro::Spacing`. Also used to
|
||||
/// guide pretty-printing, which is where the `JointHidden` value (which isn't
|
||||
|
|
@ -620,58 +596,9 @@ pub enum Spacing {
|
|||
JointHidden,
|
||||
}
|
||||
|
||||
impl TokenStream {
|
||||
/// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
|
||||
/// separating the two arguments with a comma for diagnostic suggestions.
|
||||
pub fn add_comma(&self) -> Option<(TokenStream, Span)> {
|
||||
// Used to suggest if a user writes `foo!(a b);`
|
||||
let mut suggestion = None;
|
||||
let mut iter = self.0.iter().enumerate().peekable();
|
||||
while let Some((pos, ts)) = iter.next() {
|
||||
if let Some((_, next)) = iter.peek() {
|
||||
let sp = match (&ts, &next) {
|
||||
(_, TokenTree::Token(Token { kind: token::Comma, .. }, _)) => continue,
|
||||
(
|
||||
TokenTree::Token(token_left, Spacing::Alone),
|
||||
TokenTree::Token(token_right, _),
|
||||
) if (token_left.is_non_reserved_ident() || token_left.is_lit())
|
||||
&& (token_right.is_non_reserved_ident() || token_right.is_lit()) =>
|
||||
{
|
||||
token_left.span
|
||||
}
|
||||
(TokenTree::Delimited(sp, ..), _) => sp.entire(),
|
||||
_ => continue,
|
||||
};
|
||||
let sp = sp.shrink_to_hi();
|
||||
let comma = TokenTree::token_alone(token::Comma, sp);
|
||||
suggestion = Some((pos, comma, sp));
|
||||
}
|
||||
}
|
||||
if let Some((pos, comma, sp)) = suggestion {
|
||||
let mut new_stream = Vec::with_capacity(self.0.len() + 1);
|
||||
let parts = self.0.split_at(pos + 1);
|
||||
new_stream.extend_from_slice(parts.0);
|
||||
new_stream.push(comma);
|
||||
new_stream.extend_from_slice(parts.1);
|
||||
return Some((TokenStream::new(new_stream), sp));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
|
||||
TokenStream::new(iter.into_iter().collect::<Vec<TokenTree>>())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for TokenStream {}
|
||||
|
||||
impl PartialEq<TokenStream> for TokenStream {
|
||||
fn eq(&self, other: &TokenStream) -> bool {
|
||||
self.iter().eq(other.iter())
|
||||
}
|
||||
}
|
||||
/// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s.
|
||||
#[derive(Clone, Debug, Default, Encodable, Decodable)]
|
||||
pub struct TokenStream(pub(crate) Arc<Vec<TokenTree>>);
|
||||
|
||||
impl TokenStream {
|
||||
pub fn new(tts: Vec<TokenTree>) -> TokenStream {
|
||||
|
|
@ -847,6 +774,68 @@ impl TokenStream {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
|
||||
/// separating the two arguments with a comma for diagnostic suggestions.
|
||||
pub fn add_comma(&self) -> Option<(TokenStream, Span)> {
|
||||
// Used to suggest if a user writes `foo!(a b);`
|
||||
let mut suggestion = None;
|
||||
let mut iter = self.0.iter().enumerate().peekable();
|
||||
while let Some((pos, ts)) = iter.next() {
|
||||
if let Some((_, next)) = iter.peek() {
|
||||
let sp = match (&ts, &next) {
|
||||
(_, TokenTree::Token(Token { kind: token::Comma, .. }, _)) => continue,
|
||||
(
|
||||
TokenTree::Token(token_left, Spacing::Alone),
|
||||
TokenTree::Token(token_right, _),
|
||||
) if (token_left.is_non_reserved_ident() || token_left.is_lit())
|
||||
&& (token_right.is_non_reserved_ident() || token_right.is_lit()) =>
|
||||
{
|
||||
token_left.span
|
||||
}
|
||||
(TokenTree::Delimited(sp, ..), _) => sp.entire(),
|
||||
_ => continue,
|
||||
};
|
||||
let sp = sp.shrink_to_hi();
|
||||
let comma = TokenTree::token_alone(token::Comma, sp);
|
||||
suggestion = Some((pos, comma, sp));
|
||||
}
|
||||
}
|
||||
if let Some((pos, comma, sp)) = suggestion {
|
||||
let mut new_stream = Vec::with_capacity(self.0.len() + 1);
|
||||
let parts = self.0.split_at(pos + 1);
|
||||
new_stream.extend_from_slice(parts.0);
|
||||
new_stream.push(comma);
|
||||
new_stream.extend_from_slice(parts.1);
|
||||
return Some((TokenStream::new(new_stream), sp));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<TokenStream> for TokenStream {
|
||||
fn eq(&self, other: &TokenStream) -> bool {
|
||||
self.iter().eq(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for TokenStream {}
|
||||
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
|
||||
TokenStream::new(iter.into_iter().collect::<Vec<TokenTree>>())
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX> HashStable<CTX> for TokenStream
|
||||
where
|
||||
CTX: crate::HashStableContext,
|
||||
{
|
||||
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||
for sub_tt in self.iter() {
|
||||
sub_tt.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -907,6 +896,12 @@ impl TokenTreeCursor {
|
|||
pub fn bump(&mut self) {
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
// For skipping ahead in rare circumstances.
|
||||
#[inline]
|
||||
pub fn bump_to_end(&mut self) {
|
||||
self.index = self.stream.len();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that
|
||||
|
|
|
|||
|
|
@ -929,8 +929,13 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
|
||||
impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| {
|
||||
let Impl { defaultness, safety, generics, constness, polarity, of_trait, self_ty, items } = self;
|
||||
visit_visitable!($($mut)? vis, defaultness, safety, generics, constness, polarity, of_trait, self_ty);
|
||||
let Impl { generics, of_trait, self_ty, items } = self;
|
||||
try_visit!(vis.visit_generics(generics));
|
||||
if let Some(box of_trait) = of_trait {
|
||||
let TraitImplHeader { defaultness, safety, constness, polarity, trait_ref } = of_trait;
|
||||
visit_visitable!($($mut)? vis, defaultness, safety, constness, polarity, trait_ref);
|
||||
}
|
||||
try_visit!(vis.visit_ty(self_ty));
|
||||
visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() });
|
||||
V::Result::output()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl IntTy {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn normalize(&self, target_width: u32) -> Self {
|
||||
pub fn normalize(&self, target_width: u16) -> Self {
|
||||
match self {
|
||||
IntTy::Isize => match target_width {
|
||||
16 => IntTy::I16,
|
||||
|
|
@ -148,7 +148,7 @@ impl UintTy {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn normalize(&self, target_width: u32) -> Self {
|
||||
pub fn normalize(&self, target_width: u16) -> Self {
|
||||
match self {
|
||||
UintTy::Usize => match target_width {
|
||||
16 => UintTy::U16,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Target;
|
||||
use rustc_span::sym;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
@ -109,7 +110,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let span = self.lower_span(l.span);
|
||||
let source = hir::LocalSource::Normal;
|
||||
self.lower_attrs(hir_id, &l.attrs, l.span);
|
||||
self.lower_attrs(hir_id, &l.attrs, l.span, Target::Statement);
|
||||
self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{HirId, find_attr};
|
||||
use rustc_hir::{HirId, Target, find_attr};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::errors::report_lit_error;
|
||||
|
|
@ -74,7 +74,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
if !e.attrs.is_empty() {
|
||||
let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
|
||||
let new_attrs = self
|
||||
.lower_attrs_vec(&e.attrs, e.span, ex.hir_id)
|
||||
.lower_attrs_vec(&e.attrs, e.span, ex.hir_id, Target::from_expr(e))
|
||||
.into_iter()
|
||||
.chain(old_attrs.iter().cloned());
|
||||
let new_attrs = &*self.arena.alloc_from_iter(new_attrs);
|
||||
|
|
@ -97,7 +97,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
|
||||
let expr_hir_id = self.lower_node_id(e.id);
|
||||
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span);
|
||||
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
|
||||
|
||||
let kind = match &e.kind {
|
||||
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
|
||||
|
|
@ -639,7 +639,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span, Target::Arm);
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
// We need to lower the body even if it's unneeded for never pattern in match,
|
||||
// ensure that we can get HirId for DefId if need (issue #137708).
|
||||
|
|
@ -820,6 +820,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
span: unstable_span,
|
||||
}],
|
||||
span,
|
||||
Target::Fn,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1433,10 +1434,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.dcx().emit_err(FunctionalRecordUpdateDestructuringAssignment {
|
||||
span: e.span,
|
||||
});
|
||||
true
|
||||
Some(self.lower_span(e.span))
|
||||
}
|
||||
StructRest::Rest(_) => true,
|
||||
StructRest::None => false,
|
||||
StructRest::Rest(span) => Some(self.lower_span(*span)),
|
||||
StructRest::None => None,
|
||||
};
|
||||
let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
|
||||
return self.pat_without_dbm(lhs.span, struct_pat);
|
||||
|
|
@ -1535,7 +1536,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::LangItem::Range
|
||||
}
|
||||
}
|
||||
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
|
||||
(None, Some(..), Closed) => {
|
||||
if self.tcx.features().new_range() {
|
||||
hir::LangItem::RangeToInclusiveCopy
|
||||
} else {
|
||||
hir::LangItem::RangeToInclusive
|
||||
}
|
||||
}
|
||||
(Some(e1), Some(e2), Closed) => {
|
||||
if self.tcx.features().new_range() {
|
||||
hir::LangItem::RangeInclusiveCopy
|
||||
|
|
@ -1559,13 +1566,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
};
|
||||
|
||||
let fields = self.arena.alloc_from_iter(
|
||||
e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map(
|
||||
|(s, e)| {
|
||||
e1.iter()
|
||||
.map(|e| (sym::start, e))
|
||||
.chain(e2.iter().map(|e| {
|
||||
(
|
||||
if matches!(
|
||||
lang_item,
|
||||
hir::LangItem::RangeInclusiveCopy | hir::LangItem::RangeToInclusiveCopy
|
||||
) {
|
||||
sym::last
|
||||
} else {
|
||||
sym::end
|
||||
},
|
||||
e,
|
||||
)
|
||||
}))
|
||||
.map(|(s, e)| {
|
||||
let expr = self.lower_expr(e);
|
||||
let ident = Ident::new(s, self.lower_span(e.span));
|
||||
self.expr_field(ident, expr, e.span)
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
hir::ExprKind::Struct(
|
||||
|
|
@ -1654,7 +1674,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField);
|
||||
hir::ExprField {
|
||||
hir_id,
|
||||
ident: self.lower_ident(f.ident),
|
||||
|
|
@ -1910,7 +1930,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
//
|
||||
// Also, add the attributes to the outer returned expr node.
|
||||
let expr = self.expr_drop_temps_mut(for_span, match_expr);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs, e.span);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs, e.span, Target::from_expr(e));
|
||||
expr
|
||||
}
|
||||
|
||||
|
|
@ -1967,7 +1987,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let val_ident = Ident::with_dummy_span(sym::val);
|
||||
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
|
||||
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs, span);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs, span, Target::Expression);
|
||||
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
|
||||
self.arm(continue_pat, val_expr)
|
||||
};
|
||||
|
|
@ -1998,7 +2018,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let ret_expr = self.checked_return(Some(from_residual_expr));
|
||||
self.arena.alloc(self.expr(try_span, ret_expr))
|
||||
};
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, span);
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, span, Target::Expression);
|
||||
|
||||
let break_pat = self.pat_cf_break(try_span, residual_local);
|
||||
self.arm(break_pat, ret_expr)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{DefKind, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin, find_attr};
|
||||
use rustc_hir::{
|
||||
self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, find_attr,
|
||||
};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
|
|
@ -80,7 +82,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
|||
self.with_lctx(CRATE_NODE_ID, |lctx| {
|
||||
let module = lctx.lower_mod(&c.items, &c.spans);
|
||||
// FIXME(jdonszelman): is dummy span ever a problem here?
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP, Target::Crate);
|
||||
hir::OwnerNode::Crate(module)
|
||||
})
|
||||
}
|
||||
|
|
@ -136,7 +138,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
|
||||
let vis_span = self.lower_span(i.vis.span);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));
|
||||
let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
|
||||
let item = hir::Item {
|
||||
owner_id: hir_id.expect_owner(),
|
||||
|
|
@ -251,7 +253,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ItemKind::Mod(_, ident, mod_kind) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
match mod_kind {
|
||||
ModKind::Loaded(items, _, spans, _) => {
|
||||
ModKind::Loaded(items, _, spans) => {
|
||||
hir::ItemKind::Mod(ident, self.lower_mod(items, spans))
|
||||
}
|
||||
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
|
||||
|
|
@ -340,13 +342,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
);
|
||||
hir::ItemKind::Union(ident, generics, vdata)
|
||||
}
|
||||
ItemKind::Impl(box Impl {
|
||||
safety,
|
||||
polarity,
|
||||
defaultness,
|
||||
constness,
|
||||
ItemKind::Impl(Impl {
|
||||
generics: ast_generics,
|
||||
of_trait: trait_ref,
|
||||
of_trait,
|
||||
self_ty: ty,
|
||||
items: impl_items,
|
||||
}) => {
|
||||
|
|
@ -364,54 +362,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// lifetime to be added, but rather a reference to a
|
||||
// parent lifetime.
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, (trait_ref, lowered_ty)) =
|
||||
let (generics, (of_trait, lowered_ty)) =
|
||||
self.lower_generics(ast_generics, id, itctx, |this| {
|
||||
let modifiers = TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
asyncness: BoundAsyncness::Normal,
|
||||
// we don't use this in bound lowering
|
||||
polarity: BoundPolarity::Positive,
|
||||
};
|
||||
|
||||
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
|
||||
this.lower_trait_ref(
|
||||
modifiers,
|
||||
trait_ref,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
|
||||
)
|
||||
});
|
||||
let of_trait = of_trait
|
||||
.as_deref()
|
||||
.map(|of_trait| this.lower_trait_impl_header(of_trait));
|
||||
|
||||
let lowered_ty = this.lower_ty(
|
||||
ty,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf),
|
||||
);
|
||||
|
||||
(trait_ref, lowered_ty)
|
||||
(of_trait, lowered_ty)
|
||||
});
|
||||
|
||||
let new_impl_items = self
|
||||
.arena
|
||||
.alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
|
||||
|
||||
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
|
||||
// to not cause an assertion failure inside the `lower_defaultness` function.
|
||||
let has_val = true;
|
||||
let (defaultness, defaultness_span) = self.lower_defaultness(*defaultness, has_val);
|
||||
let polarity = match polarity {
|
||||
ImplPolarity::Positive => ImplPolarity::Positive,
|
||||
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
|
||||
};
|
||||
hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
|
||||
constness: self.lower_constness(*constness),
|
||||
safety: self.lower_safety(*safety, hir::Safety::Safe),
|
||||
polarity,
|
||||
defaultness,
|
||||
defaultness_span,
|
||||
hir::ItemKind::Impl(hir::Impl {
|
||||
generics,
|
||||
of_trait: trait_ref,
|
||||
of_trait,
|
||||
self_ty: lowered_ty,
|
||||
items: new_impl_items,
|
||||
}))
|
||||
})
|
||||
}
|
||||
ItemKind::Trait(box Trait {
|
||||
constness,
|
||||
|
|
@ -464,14 +438,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let body = Box::new(self.lower_delim_args(body));
|
||||
let def_id = self.local_def_id(id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
let DefKind::Macro(macro_kind) = def_kind else {
|
||||
let DefKind::Macro(macro_kinds) = def_kind else {
|
||||
unreachable!(
|
||||
"expected DefKind::Macro for macro item, found {}",
|
||||
def_kind.descr(def_id.to_def_id())
|
||||
);
|
||||
};
|
||||
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
|
||||
hir::ItemKind::Macro(ident, macro_def, macro_kind)
|
||||
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
|
||||
}
|
||||
ItemKind::Delegation(box delegation) => {
|
||||
let delegation_results = self.lower_delegation(delegation, id, false);
|
||||
|
|
@ -649,7 +623,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let owner_id = hir_id.expect_owner();
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let attrs =
|
||||
self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_foreign_item_kind(&i.kind));
|
||||
let (ident, kind) = match &i.kind {
|
||||
ForeignItemKind::Fn(box Fn { sig, ident, generics, define_opaque, .. }) => {
|
||||
let fdec = &sig.decl;
|
||||
|
|
@ -718,7 +693,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_variant(&mut self, item_kind: &ItemKind, v: &Variant) -> hir::Variant<'hir> {
|
||||
let hir_id = self.lower_node_id(v.id);
|
||||
self.lower_attrs(hir_id, &v.attrs, v.span);
|
||||
self.lower_attrs(hir_id, &v.attrs, v.span, Target::Variant);
|
||||
hir::Variant {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(v.id),
|
||||
|
|
@ -801,7 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
) -> hir::FieldDef<'hir> {
|
||||
let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span, Target::Field);
|
||||
hir::FieldDef {
|
||||
span: self.lower_span(f.span),
|
||||
hir_id,
|
||||
|
|
@ -820,7 +795,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let attrs = self.lower_attrs(
|
||||
hir_id,
|
||||
&i.attrs,
|
||||
i.span,
|
||||
Target::from_assoc_item_kind(&i.kind, AssocCtxt::Trait),
|
||||
);
|
||||
let trait_item_def_id = hir_id.expect_owner();
|
||||
|
||||
let (ident, generics, kind, has_default) = match &i.kind {
|
||||
|
|
@ -982,6 +962,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.expr(span, hir::ExprKind::Err(guar))
|
||||
}
|
||||
|
||||
fn lower_trait_impl_header(
|
||||
&mut self,
|
||||
trait_impl_header: &TraitImplHeader,
|
||||
) -> &'hir hir::TraitImplHeader<'hir> {
|
||||
let TraitImplHeader { constness, safety, polarity, defaultness, ref trait_ref } =
|
||||
*trait_impl_header;
|
||||
let constness = self.lower_constness(constness);
|
||||
let safety = self.lower_safety(safety, hir::Safety::Safe);
|
||||
let polarity = match polarity {
|
||||
ImplPolarity::Positive => ImplPolarity::Positive,
|
||||
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)),
|
||||
};
|
||||
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
|
||||
// to not cause an assertion failure inside the `lower_defaultness` function.
|
||||
let has_val = true;
|
||||
let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val);
|
||||
let modifiers = TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
asyncness: BoundAsyncness::Normal,
|
||||
// we don't use this in bound lowering
|
||||
polarity: BoundPolarity::Positive,
|
||||
};
|
||||
let trait_ref = self.lower_trait_ref(
|
||||
modifiers,
|
||||
trait_ref,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
|
||||
);
|
||||
|
||||
self.arena.alloc(hir::TraitImplHeader {
|
||||
constness,
|
||||
safety,
|
||||
polarity,
|
||||
defaultness,
|
||||
defaultness_span,
|
||||
trait_ref,
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_impl_item(
|
||||
&mut self,
|
||||
i: &AssocItem,
|
||||
|
|
@ -991,7 +1009,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let has_value = true;
|
||||
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let attrs = self.lower_attrs(
|
||||
hir_id,
|
||||
&i.attrs,
|
||||
i.span,
|
||||
Target::from_assoc_item_kind(&i.kind, AssocCtxt::Impl { of_trait: is_in_trait_impl }),
|
||||
);
|
||||
|
||||
let (ident, (generics, kind)) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem {
|
||||
|
|
@ -1096,20 +1119,31 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
};
|
||||
|
||||
let span = self.lower_span(i.span);
|
||||
let item = hir::ImplItem {
|
||||
owner_id: hir_id.expect_owner(),
|
||||
ident: self.lower_ident(ident),
|
||||
generics,
|
||||
impl_kind: if is_in_trait_impl {
|
||||
ImplItemImplKind::Trait {
|
||||
defaultness,
|
||||
trait_item_def_id: self
|
||||
.resolver
|
||||
.get_partial_res(i.id)
|
||||
.and_then(|r| r.expect_full_res().opt_def_id())
|
||||
.ok_or_else(|| {
|
||||
self.dcx().span_delayed_bug(
|
||||
span,
|
||||
"could not resolve trait item being implemented",
|
||||
)
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
ImplItemImplKind::Inherent { vis_span: self.lower_span(i.vis.span) }
|
||||
},
|
||||
kind,
|
||||
vis_span: self.lower_span(i.vis.span),
|
||||
span: self.lower_span(i.span),
|
||||
defaultness,
|
||||
span,
|
||||
has_delayed_lints: !self.delayed_lints.is_empty(),
|
||||
trait_item_def_id: self
|
||||
.resolver
|
||||
.get_partial_res(i.id)
|
||||
.map(|r| r.expect_full_res().opt_def_id())
|
||||
.unwrap_or(None),
|
||||
};
|
||||
self.arena.alloc(item)
|
||||
}
|
||||
|
|
@ -1161,7 +1195,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span, Target::Param);
|
||||
hir::Param {
|
||||
hir_id,
|
||||
pat: self.lower_pat(¶m.pat),
|
||||
|
|
@ -1575,7 +1609,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let safety = self.lower_safety(h.safety, default_safety);
|
||||
|
||||
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
|
||||
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
|
||||
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. })
|
||||
&& safety.is_safe()
|
||||
&& !self.tcx.sess.target.is_like_wasm
|
||||
{
|
||||
|
|
@ -1841,7 +1875,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
) -> hir::WherePredicate<'hir> {
|
||||
let hir_id = self.lower_node_id(pred.id);
|
||||
let span = self.lower_span(pred.span);
|
||||
self.lower_attrs(hir_id, &pred.attrs, span);
|
||||
self.lower_attrs(hir_id, &pred.attrs, span, Target::WherePredicate);
|
||||
let kind = self.arena.alloc(match &pred.kind {
|
||||
WherePredicateKind::BoundPredicate(WhereBoundPredicate {
|
||||
bound_generic_params,
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
|||
use rustc_hir::lints::DelayedLint;
|
||||
use rustc_hir::{
|
||||
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
|
||||
LifetimeSyntax, ParamName, TraitCandidate,
|
||||
LifetimeSyntax, ParamName, Target, TraitCandidate,
|
||||
};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_macros::extension;
|
||||
|
|
@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> {
|
|||
enum RelaxedBoundForbiddenReason {
|
||||
TraitObjectTy,
|
||||
SuperTrait,
|
||||
AssocTyBounds,
|
||||
LateBoundVarsInScope,
|
||||
}
|
||||
|
||||
|
|
@ -942,11 +943,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
id: HirId,
|
||||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
target: Target,
|
||||
) -> &'hir [hir::Attribute] {
|
||||
if attrs.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id);
|
||||
let lowered_attrs =
|
||||
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
|
||||
|
||||
assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
|
|
@ -971,12 +974,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
target_hir_id: HirId,
|
||||
target: Target,
|
||||
) -> Vec<hir::Attribute> {
|
||||
let l = self.span_lowerer();
|
||||
self.attribute_parser.parse_attribute_list(
|
||||
attrs,
|
||||
target_span,
|
||||
target_hir_id,
|
||||
target,
|
||||
OmitDoc::Lower,
|
||||
|s| l.lower(s),
|
||||
|l| {
|
||||
|
|
@ -1109,9 +1114,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar)));
|
||||
hir::AssocItemConstraintKind::Equality { term: err_ty.into() }
|
||||
} else {
|
||||
// FIXME(#135229): These should be forbidden!
|
||||
let bounds =
|
||||
self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx);
|
||||
let bounds = self.lower_param_bounds(
|
||||
bounds,
|
||||
RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds),
|
||||
itctx,
|
||||
);
|
||||
hir::AssocItemConstraintKind::Bound { bounds }
|
||||
}
|
||||
}
|
||||
|
|
@ -1939,7 +1946,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let (name, kind) = self.lower_generic_param_kind(param, source);
|
||||
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span());
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span(), Target::Param);
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(param.id),
|
||||
|
|
@ -2021,7 +2028,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
(
|
||||
hir::ParamName::Plain(self.lower_ident(param.ident)),
|
||||
hir::GenericParamKind::Const { ty, default, synthetic: false },
|
||||
hir::GenericParamKind::Const { ty, default },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2094,17 +2101,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
{
|
||||
return;
|
||||
}
|
||||
if self.tcx.features().more_maybe_bounds() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
RelaxedBoundPolicy::Forbidden(reason) => {
|
||||
if self.tcx.features().more_maybe_bounds() {
|
||||
return;
|
||||
}
|
||||
|
||||
match reason {
|
||||
RelaxedBoundForbiddenReason::TraitObjectTy => {
|
||||
if self.tcx.features().more_maybe_bounds() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.dcx().span_err(
|
||||
span,
|
||||
"relaxed bounds are not permitted in trait object types",
|
||||
|
|
@ -2112,6 +2116,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
return;
|
||||
}
|
||||
RelaxedBoundForbiddenReason::SuperTrait => {
|
||||
if self.tcx.features().more_maybe_bounds() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diag = self.dcx().struct_span_err(
|
||||
span,
|
||||
"relaxed bounds are not permitted in supertrait bounds",
|
||||
|
|
@ -2124,7 +2132,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
diag.emit();
|
||||
return;
|
||||
}
|
||||
RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
|
||||
RelaxedBoundForbiddenReason::AssocTyBounds
|
||||
| RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -2500,7 +2509,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fields: &'hir [hir::PatField<'hir>],
|
||||
) -> &'hir hir::Pat<'hir> {
|
||||
let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span));
|
||||
self.pat(span, hir::PatKind::Struct(qpath, fields, false))
|
||||
self.pat(span, hir::PatKind::Struct(qpath, fields, None))
|
||||
}
|
||||
|
||||
fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
use rustc_ast::*;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_hir::{self as hir, LangItem, Target};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_span::source_map::{Spanned, respan};
|
||||
use rustc_span::{DesugaringKind, Ident, Span};
|
||||
|
|
@ -93,7 +93,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span, Target::PatField);
|
||||
|
||||
hir::PatField {
|
||||
hir_id,
|
||||
|
|
@ -106,10 +106,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
break hir::PatKind::Struct(
|
||||
qpath,
|
||||
fs,
|
||||
matches!(
|
||||
etc,
|
||||
ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_)
|
||||
),
|
||||
match etc {
|
||||
ast::PatFieldsRest::Rest(sp) => Some(self.lower_span(*sp)),
|
||||
ast::PatFieldsRest::Recovered(_) => Some(Span::default()),
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
}
|
||||
PatKind::Tuple(pats) => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ rustc_errors = { path = "../rustc_errors" }
|
|||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
|||
|
|
@ -17,9 +17,13 @@ ast_passes_abi_must_not_have_parameters_or_return_type=
|
|||
|
||||
ast_passes_abi_must_not_have_return_type=
|
||||
invalid signature for `extern {$abi}` function
|
||||
.note = functions with the "custom" ABI cannot have a return type
|
||||
.note = functions with the {$abi} ABI cannot have a return type
|
||||
.help = remove the return type
|
||||
|
||||
ast_passes_abi_x86_interrupt =
|
||||
invalid signature for `extern "x86-interrupt"` function
|
||||
.note = functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found {$param_count})
|
||||
|
||||
ast_passes_assoc_const_without_body =
|
||||
associated constant in `impl` without body
|
||||
.suggestion = provide a definition for the constant
|
||||
|
|
@ -32,6 +36,13 @@ ast_passes_assoc_type_without_body =
|
|||
associated type in `impl` without body
|
||||
.suggestion = provide a definition for the type
|
||||
|
||||
ast_passes_async_fn_in_const_trait_or_trait_impl =
|
||||
async functions are not allowed in `const` {$in_impl ->
|
||||
[true] trait impls
|
||||
*[false] traits
|
||||
}
|
||||
.label = associated functions of `const` cannot be declared `async`
|
||||
|
||||
ast_passes_at_least_one_trait = at least one trait must be specified
|
||||
|
||||
ast_passes_auto_generic = auto traits cannot have generic parameters
|
||||
|
|
@ -46,8 +57,6 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim
|
|||
.label = {ast_passes_auto_super_lifetime}
|
||||
.suggestion = remove the super traits or lifetime bounds
|
||||
|
||||
ast_passes_bad_c_variadic = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
|
||||
|
||||
ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
|
||||
.cannot_have = cannot have a body
|
||||
.invalid = the invalid body
|
||||
|
|
@ -55,6 +64,17 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
|
|||
|
||||
ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
|
||||
|
||||
ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
|
||||
.label = `extern "{$abi}"` because of this
|
||||
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
ast_passes_c_variadic_must_be_unsafe =
|
||||
functions with a C variable argument list must be unsafe
|
||||
.suggestion = add the `unsafe` keyword to this definition
|
||||
|
||||
ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions
|
||||
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
|
||||
.const = `const` because of this
|
||||
.variadic = C-variadic because of this
|
||||
|
|
@ -73,6 +93,10 @@ ast_passes_const_without_body =
|
|||
ast_passes_constraint_on_negative_bound =
|
||||
associated type constraints not allowed on negative bounds
|
||||
|
||||
ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic
|
||||
.const = `{$coroutine_kind}` because of this
|
||||
.variadic = C-variadic because of this
|
||||
|
||||
ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
|
||||
.label = not supported
|
||||
.suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax
|
||||
|
|
@ -102,6 +126,10 @@ ast_passes_extern_without_abi = `extern` declarations without an explicit ABI ar
|
|||
.suggestion = specify an ABI
|
||||
.help = prior to Rust 2024, a default ABI was inferred
|
||||
|
||||
ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated
|
||||
.label = ABI should be specified here
|
||||
.suggestion = explicitly specify the {$default_abi} ABI
|
||||
|
||||
ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
|
||||
.suggestion = remove the attribute
|
||||
.stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
|
||||
|
|
@ -175,11 +203,6 @@ ast_passes_generic_default_trailing = generic parameters with a default must be
|
|||
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
|
||||
.help = remove one of these features
|
||||
|
||||
ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
|
||||
.because = {$annotation} because of this
|
||||
.type = inherent impl for this type
|
||||
.only_trait = only trait implementations may be annotated with {$annotation}
|
||||
|
||||
ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
|
||||
.suggestion = remove safe from this item
|
||||
|
||||
|
|
@ -191,6 +214,10 @@ ast_passes_match_arm_with_no_body =
|
|||
.suggestion = add a body after the pattern
|
||||
|
||||
ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe
|
||||
.suggestion = needs `unsafe` before the extern keyword
|
||||
|
||||
ast_passes_missing_unsafe_on_extern_lint = extern blocks should be unsafe
|
||||
.suggestion = needs `unsafe` before the extern keyword
|
||||
|
||||
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
|
||||
.help = consider using the `#[path]` attribute to specify filesystem path
|
||||
|
|
|
|||
|
|
@ -25,16 +25,16 @@ use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
|
|||
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
|
||||
use rustc_ast::*;
|
||||
use rustc_ast_pretty::pprust::{self, State};
|
||||
use rustc_attr_parsing::validate_attr;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_errors::{DiagCtxtHandle, LintBuffer};
|
||||
use rustc_feature::Features;
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::{
|
||||
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
|
||||
PATTERNS_IN_FNS_WITHOUT_BODY,
|
||||
};
|
||||
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
|
||||
use rustc_span::{Ident, Span, kw, sym};
|
||||
use rustc_target::spec::{AbiMap, AbiMapping};
|
||||
use thin_vec::thin_vec;
|
||||
|
|
@ -293,6 +293,21 @@ impl<'a> AstValidator<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
fn check_async_fn_in_const_trait_or_impl(&self, sig: &FnSig, parent: &TraitOrTraitImpl) {
|
||||
let Some(const_keyword) = parent.constness() else { return };
|
||||
|
||||
let Some(CoroutineKind::Async { span: async_keyword, .. }) = sig.header.coroutine_kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl {
|
||||
async_keyword,
|
||||
in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
|
||||
const_keyword,
|
||||
});
|
||||
}
|
||||
|
||||
fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
|
||||
self.check_decl_num_args(fn_decl);
|
||||
self.check_decl_cvariadic_pos(fn_decl);
|
||||
|
|
@ -390,7 +405,24 @@ impl<'a> AstValidator<'a> {
|
|||
if let InterruptKind::X86 = interrupt_kind {
|
||||
// "x86-interrupt" is special because it does have arguments.
|
||||
// FIXME(workingjubilee): properly lint on acceptable input types.
|
||||
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
|
||||
let inputs = &sig.decl.inputs;
|
||||
let param_count = inputs.len();
|
||||
if !matches!(param_count, 1 | 2) {
|
||||
let mut spans: Vec<Span> =
|
||||
inputs.iter().map(|arg| arg.span).collect();
|
||||
if spans.is_empty() {
|
||||
spans = vec![sig.span];
|
||||
}
|
||||
self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count });
|
||||
}
|
||||
|
||||
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
|
||||
&& match &ret_ty.kind {
|
||||
TyKind::Never => false,
|
||||
TyKind::Tup(tup) if tup.is_empty() => false,
|
||||
_ => true,
|
||||
}
|
||||
{
|
||||
self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
|
||||
span: ret_ty.span,
|
||||
abi,
|
||||
|
|
@ -449,12 +481,18 @@ impl<'a> AstValidator<'a> {
|
|||
|
||||
fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
|
||||
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
|
||||
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
|
||||
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
|
||||
&& match &ret_ty.kind {
|
||||
TyKind::Never => false,
|
||||
TyKind::Tup(tup) if tup.is_empty() => false,
|
||||
_ => true,
|
||||
}
|
||||
{
|
||||
spans.push(ret_ty.span);
|
||||
}
|
||||
|
||||
if !spans.is_empty() {
|
||||
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
|
||||
let header_span = sig.header_span();
|
||||
let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
|
||||
let padding = if header_span.is_empty() { "" } else { " " };
|
||||
|
||||
|
|
@ -627,46 +665,68 @@ impl<'a> AstValidator<'a> {
|
|||
/// - Non-const
|
||||
/// - Either foreign, or free and `unsafe extern "C"` semantically
|
||||
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
|
||||
let variadic_spans: Vec<_> = fk
|
||||
.decl()
|
||||
.inputs
|
||||
.iter()
|
||||
.filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs))
|
||||
.map(|arg| arg.span)
|
||||
.collect();
|
||||
// `...` is already rejected when it is not the final parameter.
|
||||
let variadic_param = match fk.decl().inputs.last() {
|
||||
Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if variadic_spans.is_empty() {
|
||||
return;
|
||||
}
|
||||
let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else {
|
||||
// Unreachable because the parser already rejects `...` in closures.
|
||||
unreachable!("C variable argument list cannot be used in closures")
|
||||
};
|
||||
|
||||
if let Some(header) = fk.header()
|
||||
&& let Const::Yes(const_span) = header.constness
|
||||
{
|
||||
let mut spans = variadic_spans.clone();
|
||||
spans.push(const_span);
|
||||
// C-variadics are not yet implemented in const evaluation.
|
||||
if let Const::Yes(const_span) = sig.header.constness {
|
||||
self.dcx().emit_err(errors::ConstAndCVariadic {
|
||||
spans,
|
||||
spans: vec![const_span, variadic_param.span],
|
||||
const_span,
|
||||
variadic_spans: variadic_spans.clone(),
|
||||
variadic_span: variadic_param.span,
|
||||
});
|
||||
}
|
||||
|
||||
match (fk.ctxt(), fk.header()) {
|
||||
(Some(FnCtxt::Foreign), _) => return,
|
||||
(Some(FnCtxt::Free), Some(header)) => match header.ext {
|
||||
Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
|
||||
| Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _)
|
||||
| Extern::Implicit(_)
|
||||
if matches!(header.safety, Safety::Unsafe(_)) =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
if let Some(coroutine_kind) = sig.header.coroutine_kind {
|
||||
self.dcx().emit_err(errors::CoroutineAndCVariadic {
|
||||
spans: vec![coroutine_kind.span(), variadic_param.span],
|
||||
coroutine_kind: coroutine_kind.as_str(),
|
||||
coroutine_span: coroutine_kind.span(),
|
||||
variadic_span: variadic_param.span,
|
||||
});
|
||||
}
|
||||
|
||||
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
|
||||
match fn_ctxt {
|
||||
FnCtxt::Foreign => return,
|
||||
FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext {
|
||||
Extern::Implicit(_) => {
|
||||
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
|
||||
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
|
||||
span: variadic_param.span,
|
||||
unsafe_span: sig.safety_span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
|
||||
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
|
||||
self.dcx().emit_err(errors::CVariadicBadExtern {
|
||||
span: variadic_param.span,
|
||||
abi: symbol_unescaped,
|
||||
extern_span: sig.extern_span(),
|
||||
});
|
||||
}
|
||||
|
||||
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
|
||||
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
|
||||
span: variadic_param.span,
|
||||
unsafe_span: sig.safety_span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Extern::None => {
|
||||
let err = errors::CVariadicNoExtern { span: variadic_param.span };
|
||||
self.dcx().emit_err(err);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_named(&self, ident: Ident, kind: &str) {
|
||||
|
|
@ -838,7 +898,7 @@ impl<'a> AstValidator<'a> {
|
|||
MISSING_ABI,
|
||||
id,
|
||||
span,
|
||||
BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
|
||||
errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -954,13 +1014,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Impl(box Impl {
|
||||
safety,
|
||||
polarity,
|
||||
defaultness: _,
|
||||
constness,
|
||||
ItemKind::Impl(Impl {
|
||||
generics,
|
||||
of_trait: Some(t),
|
||||
of_trait:
|
||||
Some(box TraitImplHeader {
|
||||
safety,
|
||||
polarity,
|
||||
defaultness: _,
|
||||
constness,
|
||||
trait_ref: t,
|
||||
}),
|
||||
self_ty,
|
||||
items,
|
||||
}) => {
|
||||
|
|
@ -992,46 +1055,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
|
||||
});
|
||||
}
|
||||
ItemKind::Impl(box Impl {
|
||||
safety,
|
||||
polarity,
|
||||
defaultness,
|
||||
constness,
|
||||
generics,
|
||||
of_trait: None,
|
||||
self_ty,
|
||||
items,
|
||||
}) => {
|
||||
let error = |annotation_span, annotation, only_trait| errors::InherentImplCannot {
|
||||
span: self_ty.span,
|
||||
annotation_span,
|
||||
annotation,
|
||||
self_ty: self_ty.span,
|
||||
only_trait,
|
||||
};
|
||||
|
||||
ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items }) => {
|
||||
self.visit_attrs_vis(&item.attrs, &item.vis);
|
||||
self.visibility_not_permitted(
|
||||
&item.vis,
|
||||
errors::VisibilityNotPermittedNote::IndividualImplItems,
|
||||
);
|
||||
if let &Safety::Unsafe(span) = safety {
|
||||
self.dcx().emit_err(errors::InherentImplCannotUnsafe {
|
||||
span: self_ty.span,
|
||||
annotation_span: span,
|
||||
annotation: "unsafe",
|
||||
self_ty: self_ty.span,
|
||||
});
|
||||
}
|
||||
if let &ImplPolarity::Negative(span) = polarity {
|
||||
self.dcx().emit_err(error(span, "negative", false));
|
||||
}
|
||||
if let &Defaultness::Default(def_span) = defaultness {
|
||||
self.dcx().emit_err(error(def_span, "`default`", true));
|
||||
}
|
||||
if let &Const::Yes(span) = constness {
|
||||
self.dcx().emit_err(error(span, "`const`", true));
|
||||
}
|
||||
|
||||
self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
|
||||
this.visit_generics(generics)
|
||||
|
|
@ -1097,7 +1126,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
MISSING_UNSAFE_ON_EXTERN,
|
||||
item.id,
|
||||
item.span,
|
||||
BuiltinLintDiag::MissingUnsafeOnExtern {
|
||||
errors::MissingUnsafeOnExternLint {
|
||||
suggestion: item.span.shrink_to_lo(),
|
||||
},
|
||||
);
|
||||
|
|
@ -1173,7 +1202,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
|
||||
}
|
||||
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
|
||||
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
|
||||
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
|
||||
&& !attr::contains_name(&item.attrs, sym::path)
|
||||
{
|
||||
self.check_mod_file_item_asciionly(*ident);
|
||||
|
|
@ -1597,6 +1626,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl);
|
||||
if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
|
||||
self.check_trait_fn_not_const(sig.header.constness, parent);
|
||||
self.check_async_fn_in_const_trait_or_impl(sig, parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1772,7 +1802,7 @@ fn deny_equality_constraints(
|
|||
.map(|segment| segment.ident.name)
|
||||
.zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name))
|
||||
.all(|(a, b)| a == b)
|
||||
&& let Some(potential_assoc) = full_path.segments.iter().last()
|
||||
&& let Some(potential_assoc) = full_path.segments.last()
|
||||
{
|
||||
suggest(poly, potential_assoc, predicate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
|
|||
use rustc_ast::ParamKindOrd;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
|
@ -62,6 +62,16 @@ pub(crate) struct TraitFnConst {
|
|||
pub make_trait_const_sugg: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_async_fn_in_const_trait_or_trait_impl)]
|
||||
pub(crate) struct AsyncFnInConstTraitOrTraitImpl {
|
||||
#[primary_span]
|
||||
pub async_keyword: Span,
|
||||
pub in_impl: bool,
|
||||
#[label]
|
||||
pub const_keyword: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_forbidden_bound)]
|
||||
pub(crate) struct ForbiddenBound {
|
||||
|
|
@ -309,10 +319,37 @@ pub(crate) struct ExternItemAscii {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_bad_c_variadic)]
|
||||
pub(crate) struct BadCVariadic {
|
||||
#[diag(ast_passes_c_variadic_no_extern)]
|
||||
#[help]
|
||||
pub(crate) struct CVariadicNoExtern {
|
||||
#[primary_span]
|
||||
pub span: Vec<Span>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_c_variadic_must_be_unsafe)]
|
||||
pub(crate) struct CVariadicMustBeUnsafe {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
#[suggestion(
|
||||
ast_passes_suggestion,
|
||||
applicability = "maybe-incorrect",
|
||||
code = "unsafe ",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub unsafe_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_c_variadic_bad_extern)]
|
||||
#[help]
|
||||
pub(crate) struct CVariadicBadExtern {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub abi: Symbol,
|
||||
#[label]
|
||||
pub extern_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -464,32 +501,6 @@ pub(crate) struct UnsafeNegativeImpl {
|
|||
pub r#unsafe: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_inherent_cannot_be)]
|
||||
pub(crate) struct InherentImplCannot<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label(ast_passes_because)]
|
||||
pub annotation_span: Span,
|
||||
pub annotation: &'a str,
|
||||
#[label(ast_passes_type)]
|
||||
pub self_ty: Span,
|
||||
#[note(ast_passes_only_trait)]
|
||||
pub only_trait: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_inherent_cannot_be, code = E0197)]
|
||||
pub(crate) struct InherentImplCannotUnsafe<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label(ast_passes_because)]
|
||||
pub annotation_span: Span,
|
||||
pub annotation: &'a str,
|
||||
#[label(ast_passes_type)]
|
||||
pub self_ty: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_unsafe_item)]
|
||||
pub(crate) struct UnsafeItem {
|
||||
|
|
@ -505,6 +516,13 @@ pub(crate) struct MissingUnsafeOnExtern {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(ast_passes_missing_unsafe_on_extern_lint)]
|
||||
pub(crate) struct MissingUnsafeOnExternLint {
|
||||
#[suggestion(code = "unsafe ", applicability = "machine-applicable")]
|
||||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_fieldless_union)]
|
||||
pub(crate) struct FieldlessUnion {
|
||||
|
|
@ -672,7 +690,19 @@ pub(crate) struct ConstAndCVariadic {
|
|||
#[label(ast_passes_const)]
|
||||
pub const_span: Span,
|
||||
#[label(ast_passes_variadic)]
|
||||
pub variadic_spans: Vec<Span>,
|
||||
pub variadic_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_coroutine_and_c_variadic)]
|
||||
pub(crate) struct CoroutineAndCVariadic {
|
||||
#[primary_span]
|
||||
pub spans: Vec<Span>,
|
||||
pub coroutine_kind: &'static str,
|
||||
#[label(ast_passes_const)]
|
||||
pub coroutine_span: Span,
|
||||
#[label(ast_passes_variadic)]
|
||||
pub variadic_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -831,6 +861,14 @@ pub(crate) struct MissingAbi {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(ast_passes_extern_without_abi_sugg)]
|
||||
pub(crate) struct MissingAbiSugg {
|
||||
#[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
|
||||
pub span: Span,
|
||||
pub default_abi: ExternAbi,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_abi_custom_safe_foreign_function)]
|
||||
pub(crate) struct AbiCustomSafeForeignFunction {
|
||||
|
|
@ -907,3 +945,12 @@ pub(crate) struct AbiMustNotHaveReturnType {
|
|||
pub span: Span,
|
||||
pub abi: ExternAbi,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_abi_x86_interrupt)]
|
||||
#[note]
|
||||
pub(crate) struct AbiX86Interrupt {
|
||||
#[primary_span]
|
||||
pub spans: Vec<Span>,
|
||||
pub param_count: usize,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
notable_trait => doc_notable_trait
|
||||
}
|
||||
"meant for internal use only" {
|
||||
attribute => rustdoc_internals
|
||||
keyword => rustdoc_internals
|
||||
fake_variadic => rustdoc_internals
|
||||
search_unbox => rustdoc_internals
|
||||
|
|
@ -217,18 +218,18 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => {
|
||||
if let &ast::ImplPolarity::Negative(span) = polarity {
|
||||
ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) => {
|
||||
if let ast::ImplPolarity::Negative(span) = of_trait.polarity {
|
||||
gate!(
|
||||
&self,
|
||||
negative_impls,
|
||||
span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
|
||||
span.to(of_trait.trait_ref.path.span),
|
||||
"negative trait bounds are not fully implemented; \
|
||||
use marker types for now"
|
||||
);
|
||||
}
|
||||
|
||||
if let ast::Defaultness::Default(_) = defaultness {
|
||||
if let ast::Defaultness::Default(_) = of_trait.defaultness {
|
||||
gate!(&self, specialization, i.span, "specialization is unstable");
|
||||
}
|
||||
}
|
||||
|
|
@ -524,6 +525,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
|
||||
gate_all!(super_let, "`super let` is experimental");
|
||||
gate_all!(frontmatter, "frontmatters are experimental");
|
||||
gate_all!(coroutines, "coroutine syntax is experimental");
|
||||
|
||||
if !visitor.features.never_patterns() {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::borrow::Cow;
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
|
|
@ -441,7 +441,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
|
||||
|
||||
fn print_ident(&mut self, ident: Ident) {
|
||||
self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
|
||||
self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string());
|
||||
self.ann_post(ident)
|
||||
}
|
||||
|
||||
|
|
@ -1015,17 +1015,16 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
|
||||
/* Name components */
|
||||
token::Ident(name, is_raw) => {
|
||||
IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
|
||||
IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
token::NtIdent(ident, is_raw) => {
|
||||
IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
|
||||
IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
|
||||
}
|
||||
|
||||
token::Lifetime(name, IdentIsRaw::No)
|
||||
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
|
||||
token::Lifetime(name, IdentIsRaw::Yes)
|
||||
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
|
||||
format!("'r#{}", &name.as_str()[1..]).into()
|
||||
token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => {
|
||||
IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
|
||||
}
|
||||
|
||||
/* Other */
|
||||
|
|
@ -1770,7 +1769,7 @@ impl<'a> State<'a> {
|
|||
},
|
||||
|f| f.pat.span,
|
||||
);
|
||||
if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc {
|
||||
if let ast::PatFieldsRest::Rest(_) | ast::PatFieldsRest::Recovered(_) = etc {
|
||||
if !fields.is_empty() {
|
||||
self.word_space(",");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -308,39 +308,41 @@ impl<'a> State<'a> {
|
|||
let (cb, ib) = self.head(visibility_qualified(&item.vis, "union"));
|
||||
self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
|
||||
}
|
||||
ast::ItemKind::Impl(box ast::Impl {
|
||||
safety,
|
||||
polarity,
|
||||
defaultness,
|
||||
constness,
|
||||
generics,
|
||||
of_trait,
|
||||
self_ty,
|
||||
items,
|
||||
}) => {
|
||||
ast::ItemKind::Impl(ast::Impl { generics, of_trait, self_ty, items }) => {
|
||||
let (cb, ib) = self.head("");
|
||||
self.print_visibility(&item.vis);
|
||||
self.print_defaultness(*defaultness);
|
||||
self.print_safety(*safety);
|
||||
self.word("impl");
|
||||
|
||||
if generics.params.is_empty() {
|
||||
self.nbsp();
|
||||
} else {
|
||||
self.print_generic_params(&generics.params);
|
||||
self.space();
|
||||
}
|
||||
let impl_generics = |this: &mut Self| {
|
||||
this.word("impl");
|
||||
|
||||
self.print_constness(*constness);
|
||||
if generics.params.is_empty() {
|
||||
this.nbsp();
|
||||
} else {
|
||||
this.print_generic_params(&generics.params);
|
||||
this.space();
|
||||
}
|
||||
};
|
||||
|
||||
if let ast::ImplPolarity::Negative(_) = polarity {
|
||||
self.word("!");
|
||||
}
|
||||
|
||||
if let Some(t) = of_trait {
|
||||
self.print_trait_ref(t);
|
||||
if let Some(box of_trait) = of_trait {
|
||||
let ast::TraitImplHeader {
|
||||
defaultness,
|
||||
safety,
|
||||
constness,
|
||||
polarity,
|
||||
ref trait_ref,
|
||||
} = *of_trait;
|
||||
self.print_defaultness(defaultness);
|
||||
self.print_safety(safety);
|
||||
impl_generics(self);
|
||||
self.print_constness(constness);
|
||||
if let ast::ImplPolarity::Negative(_) = polarity {
|
||||
self.word("!");
|
||||
}
|
||||
self.print_trait_ref(trait_ref);
|
||||
self.space();
|
||||
self.word_space("for");
|
||||
} else {
|
||||
impl_generics(self);
|
||||
}
|
||||
|
||||
self.print_type(self_ty);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
|||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
thin-vec = "0.2.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ attr_parsing_empty_attribute =
|
|||
unused attribute
|
||||
.suggestion = remove this attribute
|
||||
|
||||
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
|
||||
.help = `#[{$name}]` can {$only}be applied to {$applied}
|
||||
.suggestion = remove the attribute
|
||||
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
|
||||
.warn = {-attr_parsing_previously_accepted}
|
||||
.help = `#[{$name}]` can {$only}be applied to {$applied}
|
||||
.suggestion = remove the attribute
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
attr_parsing_expected_one_cfg_pattern =
|
||||
|
|
@ -78,6 +86,12 @@ attr_parsing_invalid_repr_hint_no_value =
|
|||
attr_parsing_invalid_since =
|
||||
'since' must be a Rust version number, such as "1.31.0"
|
||||
|
||||
attr_parsing_invalid_style = {$is_used_as_inner ->
|
||||
[false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]`
|
||||
*[other] the `#![{$name}]` attribute can only be used at the crate root
|
||||
}
|
||||
.note = This attribute does not have an `!`, which means it is applied to this {$target}
|
||||
|
||||
attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
|
||||
.note = the value may not exceed `u16::MAX`
|
||||
|
||||
|
|
@ -108,6 +122,14 @@ attr_parsing_null_on_export = `export_name` may not contain null characters
|
|||
|
||||
attr_parsing_null_on_link_section = `link_section` may not contain null characters
|
||||
|
||||
attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters
|
||||
|
||||
attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters
|
||||
|
||||
attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal
|
||||
|
||||
attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal
|
||||
|
||||
attr_parsing_repr_ident =
|
||||
meta item in `repr` must be an identifier
|
||||
|
||||
|
|
@ -132,11 +154,12 @@ attr_parsing_unknown_version_literal =
|
|||
attr_parsing_unrecognized_repr_hint =
|
||||
unrecognized representation hint
|
||||
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
|
||||
.note = for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
|
||||
|
||||
attr_parsing_unstable_cfg_target_compact =
|
||||
compact `cfg(target(..))` is experimental and subject to change
|
||||
|
||||
attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable
|
||||
attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable
|
||||
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
|
||||
|
||||
attr_parsing_unsupported_literal_cfg_boolean =
|
||||
|
|
@ -161,3 +184,78 @@ attr_parsing_unused_multiple =
|
|||
|
||||
-attr_parsing_previously_accepted =
|
||||
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
|
||||
attr_parsing_meta_bad_delim = wrong meta list delimiters
|
||||
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
|
||||
|
||||
attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
|
||||
.label = usage of unsafe attribute
|
||||
attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
|
||||
|
||||
attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
|
||||
.label = this is not an unsafe attribute
|
||||
.suggestion = remove the `unsafe(...)`
|
||||
.note = extraneous unsafe is not allowed in attributes
|
||||
|
||||
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
|
||||
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
|
||||
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
|
||||
|
||||
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
|
||||
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
|
||||
|
||||
attr_parsing_as_needed_compatibility =
|
||||
linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds
|
||||
|
||||
attr_parsing_bundle_needs_static =
|
||||
linking modifier `bundle` is only compatible with `static` linking kind
|
||||
|
||||
attr_parsing_empty_link_name =
|
||||
link name must not be empty
|
||||
.label = empty link name
|
||||
|
||||
attr_parsing_import_name_type_raw =
|
||||
import name type can only be used with link kind `raw-dylib`
|
||||
|
||||
attr_parsing_import_name_type_x86 =
|
||||
import name type is only supported on x86
|
||||
|
||||
attr_parsing_incompatible_wasm_link =
|
||||
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes
|
||||
|
||||
attr_parsing_invalid_link_modifier =
|
||||
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
|
||||
|
||||
attr_parsing_link_arg_unstable =
|
||||
link kind `link-arg` is unstable
|
||||
|
||||
attr_parsing_link_cfg_unstable =
|
||||
link cfg is unstable
|
||||
|
||||
attr_parsing_link_framework_apple =
|
||||
link kind `framework` is only supported on Apple targets
|
||||
|
||||
attr_parsing_link_requires_name =
|
||||
`#[link]` attribute requires a `name = "string"` argument
|
||||
.label = missing `name` argument
|
||||
|
||||
attr_parsing_multiple_modifiers =
|
||||
multiple `{$modifier}` modifiers in a single `modifiers` argument
|
||||
|
||||
attr_parsing_multiple_renamings =
|
||||
multiple renamings were specified for library `{$lib_name}`
|
||||
attr_parsing_raw_dylib_no_nul =
|
||||
link name must not contain NUL characters if link kind is `raw-dylib`
|
||||
|
||||
attr_parsing_raw_dylib_elf_unstable =
|
||||
link kind `raw-dylib` is unstable on ELF platforms
|
||||
|
||||
attr_parsing_raw_dylib_only_windows =
|
||||
link kind `raw-dylib` is only supported on Windows targets
|
||||
|
||||
attr_parsing_whole_archive_needs_static =
|
||||
linking modifier `whole-archive` is only compatible with `static` linking kind
|
||||
|
||||
attr_parsing_limit_invalid =
|
||||
`limit` must be a non-negative integer
|
||||
.label = {$error_str}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
use std::iter;
|
||||
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub(crate) struct AllowInternalUnstableParser;
|
||||
|
|
@ -15,7 +9,13 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
|||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> =
|
||||
|items, span| AttributeKind::AllowInternalUnstable(items, span);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Fn),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -32,7 +32,12 @@ impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
|
|||
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Trait),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -53,7 +58,14 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
|
|||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> =
|
||||
|items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
//! Attributes that can be found in function body.
|
||||
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct CoroutineParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for CoroutineParser {
|
||||
const PATH: &[Symbol] = &[sym::coroutine];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]);
|
||||
const CREATE: fn(rustc_span::Span) -> AttributeKind = |span| AttributeKind::Coroutine(span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ use crate::{
|
|||
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
|
||||
};
|
||||
|
||||
pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate");
|
||||
pub const CFG_TEMPLATE: AttributeTemplate = template!(
|
||||
List: &["predicate"],
|
||||
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
|
||||
);
|
||||
|
||||
pub fn parse_cfg_attr<'c, S: Stage>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -33,7 +36,7 @@ pub fn parse_cfg_attr<'c, S: Stage>(
|
|||
parse_cfg_entry(cx, single)
|
||||
}
|
||||
|
||||
fn parse_cfg_entry<S: Stage>(
|
||||
pub(crate) fn parse_cfg_entry<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
item: &MetaItemOrLitParser<'_>,
|
||||
) -> Option<CfgEntry> {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
|
||||
use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{
|
||||
AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
|
||||
NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::{
|
||||
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
|
||||
ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
|
||||
};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
|
||||
|
||||
pub(crate) struct OptimizeParser;
|
||||
|
||||
|
|
@ -17,7 +13,14 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
|
|||
const PATH: &[Symbol] = &[sym::optimize];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
|
|
@ -35,7 +38,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
|
|||
Some(sym::speed) => OptimizeAttr::Speed,
|
||||
Some(sym::none) => OptimizeAttr::DoNotOptimize,
|
||||
_ => {
|
||||
cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
|
||||
cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
|
||||
OptimizeAttr::Default
|
||||
}
|
||||
};
|
||||
|
|
@ -49,6 +52,15 @@ pub(crate) struct ColdParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
|
||||
const PATH: &[Symbol] = &[sym::cold];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::Closure),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
|
||||
}
|
||||
|
||||
|
|
@ -58,11 +70,22 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
|
|||
const PATH: &[Symbol] = &[sym::coverage];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::Crate),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(args) = args.list() else {
|
||||
cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
|
||||
cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
|
||||
return None;
|
||||
};
|
||||
|
||||
|
|
@ -71,7 +94,8 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
|
|||
return None;
|
||||
};
|
||||
|
||||
let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
|
||||
let fail_incorrect_argument =
|
||||
|span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
|
||||
|
||||
let Some(arg) = arg.meta_item() else {
|
||||
fail_incorrect_argument(args.span);
|
||||
|
|
@ -97,6 +121,17 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
|
|||
const PATH: &[rustc_span::Symbol] = &[sym::export_name];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::MacroDef),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
|
|
@ -118,6 +153,70 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ObjcClassParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
let Some(classname) = nv.value_as_str() else {
|
||||
// `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail
|
||||
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
|
||||
// Use a custom error message instead.
|
||||
cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
|
||||
return None;
|
||||
};
|
||||
if classname.as_str().contains('\0') {
|
||||
// `#[rustc_objc_class = ...]` will be converted to a null-terminated string,
|
||||
// so it may not contain any null characters.
|
||||
cx.emit_err(NullOnObjcClass { span: nv.value_span });
|
||||
return None;
|
||||
}
|
||||
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ObjcSelectorParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
let Some(methname) = nv.value_as_str() else {
|
||||
// `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail
|
||||
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
|
||||
// Use a custom error message instead.
|
||||
cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
|
||||
return None;
|
||||
};
|
||||
if methname.as_str().contains('\0') {
|
||||
// `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,
|
||||
// so it may not contain any null characters.
|
||||
cx.emit_err(NullOnObjcSelector { span: nv.value_span });
|
||||
return None;
|
||||
}
|
||||
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NakedParser {
|
||||
span: Option<Span>,
|
||||
|
|
@ -138,6 +237,13 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
|
|||
this.span = Some(cx.attr_span);
|
||||
}
|
||||
})];
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
|
||||
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
// FIXME(jdonszelmann): upgrade this list to *parsed* attributes
|
||||
|
|
@ -179,6 +285,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
|
|||
sym::rustc_std_internal_symbol,
|
||||
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
|
||||
sym::rustc_align,
|
||||
sym::rustc_align_static,
|
||||
// obviously compatible with self
|
||||
sym::naked,
|
||||
// documentation
|
||||
|
|
@ -230,6 +337,19 @@ pub(crate) struct TrackCallerParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
|
||||
const PATH: &[Symbol] = &[sym::track_caller];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::Closure),
|
||||
Warn(Target::MacroDef),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +357,12 @@ pub(crate) struct NoMangleParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
|
||||
const PATH: &[Symbol] = &[sym::no_mangle];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
|
||||
}
|
||||
|
||||
|
|
@ -253,7 +379,7 @@ pub(crate) struct UsedParser {
|
|||
impl<S: Stage> AttributeParser<S> for UsedParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::used],
|
||||
template!(Word, List: "compiler|linker"),
|
||||
template!(Word, List: &["compiler", "linker"]),
|
||||
|group: &mut Self, cx, args| {
|
||||
let used_by = match args {
|
||||
ArgParser::NoArgs => UsedBy::Linker,
|
||||
|
|
@ -289,7 +415,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
|
|||
UsedBy::Linker
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
|
||||
cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +436,8 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
|
|||
}
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
// Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
|
||||
|
|
@ -321,56 +449,207 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_tf_attribute<'c, S: Stage>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
|
||||
let mut features = Vec::new();
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return features;
|
||||
};
|
||||
if list.is_empty() {
|
||||
cx.warn_empty_attribute(cx.attr_span);
|
||||
return features;
|
||||
}
|
||||
for item in list.mixed() {
|
||||
let Some(name_value) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
|
||||
// Validate name
|
||||
let Some(name) = name_value.path().word_sym() else {
|
||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
if name != sym::enable {
|
||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||
return features;
|
||||
}
|
||||
|
||||
// Use value
|
||||
let Some(name_value) = name_value.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
let Some(value_str) = name_value.value_as_str() else {
|
||||
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
|
||||
return features;
|
||||
};
|
||||
for feature in value_str.as_str().split(",") {
|
||||
features.push((Symbol::intern(feature), item.span()));
|
||||
}
|
||||
}
|
||||
features
|
||||
}
|
||||
|
||||
pub(crate) struct TargetFeatureParser;
|
||||
|
||||
impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
||||
type Item = (Symbol, Span);
|
||||
const PATH: &[Symbol] = &[sym::target_feature];
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
|
||||
features: items,
|
||||
attr_span: span,
|
||||
was_forced: false,
|
||||
};
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
let mut features = Vec::new();
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return features;
|
||||
};
|
||||
if list.is_empty() {
|
||||
cx.warn_empty_attribute(cx.attr_span);
|
||||
return features;
|
||||
}
|
||||
for item in list.mixed() {
|
||||
let Some(name_value) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
parse_tf_attribute(cx, args)
|
||||
}
|
||||
|
||||
// Validate name
|
||||
let Some(name) = name_value.path().word_sym() else {
|
||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
if name != sym::enable {
|
||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||
return features;
|
||||
}
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Warn(Target::Statement),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::MacroDef),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
}
|
||||
|
||||
// Use value
|
||||
let Some(name_value) = name_value.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||
return features;
|
||||
};
|
||||
let Some(value_str) = name_value.value_as_str() else {
|
||||
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
|
||||
return features;
|
||||
};
|
||||
for feature in value_str.as_str().split(",") {
|
||||
features.push((Symbol::intern(feature), item.span()));
|
||||
}
|
||||
}
|
||||
features
|
||||
pub(crate) struct ForceTargetFeatureParser;
|
||||
|
||||
impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
|
||||
type Item = (Symbol, Span);
|
||||
const PATH: &[Symbol] = &[sym::force_target_feature];
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
|
||||
features: items,
|
||||
attr_span: span,
|
||||
was_forced: true,
|
||||
};
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
parse_tf_attribute(cx, args)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SanitizeParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
|
||||
const PATH: &[Symbol] = &[sym::sanitize];
|
||||
|
||||
// FIXME: still checked in check_attrs.rs
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[
|
||||
r#"address = "on|off""#,
|
||||
r#"kernel_address = "on|off""#,
|
||||
r#"cfi = "on|off""#,
|
||||
r#"hwaddress = "on|off""#,
|
||||
r#"kcfi = "on|off""#,
|
||||
r#"memory = "on|off""#,
|
||||
r#"memtag = "on|off""#,
|
||||
r#"shadow_call_stack = "on|off""#,
|
||||
r#"thread = "on|off""#
|
||||
]);
|
||||
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut on_set = SanitizerSet::empty();
|
||||
let mut off_set = SanitizerSet::empty();
|
||||
|
||||
for item in list.mixed() {
|
||||
let Some(item) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let path = item.path().word_sym();
|
||||
let Some(value) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), path);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut apply = |s: SanitizerSet| {
|
||||
let is_on = match value.value_as_str() {
|
||||
Some(sym::on) => true,
|
||||
Some(sym::off) => false,
|
||||
Some(_) => {
|
||||
cx.expected_specific_argument_strings(
|
||||
value.value_span,
|
||||
&[sym::on, sym::off],
|
||||
);
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if is_on {
|
||||
on_set |= s;
|
||||
} else {
|
||||
off_set |= s;
|
||||
}
|
||||
};
|
||||
|
||||
match path {
|
||||
Some(sym::address) | Some(sym::kernel_address) => {
|
||||
apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
|
||||
}
|
||||
Some(sym::cfi) => apply(SanitizerSet::CFI),
|
||||
Some(sym::kcfi) => apply(SanitizerSet::KCFI),
|
||||
Some(sym::memory) => apply(SanitizerSet::MEMORY),
|
||||
Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
|
||||
Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
|
||||
Some(sym::thread) => apply(SanitizerSet::THREAD),
|
||||
Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
|
||||
_ => {
|
||||
cx.expected_specific_argument_strings(
|
||||
item.path().span(),
|
||||
&[
|
||||
sym::address,
|
||||
sym::cfi,
|
||||
sym::kcfi,
|
||||
sym::memory,
|
||||
sym::memtag,
|
||||
sym::shadow_call_stack,
|
||||
sym::thread,
|
||||
sym::hwaddress,
|
||||
],
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
use rustc_feature::template;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::{FinalizeContext, Stage};
|
||||
use crate::session_diagnostics;
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::EmptyConfusables;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConfusablesParser {
|
||||
|
|
@ -16,7 +10,7 @@ pub(crate) struct ConfusablesParser {
|
|||
impl<S: Stage> AttributeParser<S> for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::rustc_confusables],
|
||||
template!(List: r#""name1", "name2", ..."#),
|
||||
template!(List: &[r#""name1", "name2", ..."#]),
|
||||
|this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
|
|
@ -24,7 +18,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
|
|||
};
|
||||
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
cx.emit_err(EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
|
|
@ -41,6 +35,8 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
|
|||
this.first_span.get_or_insert(cx.attr_span);
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.confusables.is_empty() {
|
||||
|
|
|
|||
185
compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Normal file
185
compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
use std::num::IntErrorKind;
|
||||
|
||||
use rustc_hir::limit::Limit;
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::LimitInvalid;
|
||||
|
||||
impl<S: Stage> AcceptContext<'_, '_, S> {
|
||||
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
|
||||
let Some(limit) = nv.value_as_str() else {
|
||||
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
let error_str = match limit.as_str().parse() {
|
||||
Ok(i) => return Some(Limit::new(i)),
|
||||
Err(e) => match e.kind() {
|
||||
IntErrorKind::PosOverflow => "`limit` is too large",
|
||||
IntErrorKind::Empty => "`limit` must be a non-negative integer",
|
||||
IntErrorKind::InvalidDigit => "not a valid integer",
|
||||
IntErrorKind::NegOverflow => {
|
||||
panic!(
|
||||
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
|
||||
)
|
||||
}
|
||||
IntErrorKind::Zero => {
|
||||
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
|
||||
}
|
||||
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
|
||||
},
|
||||
};
|
||||
|
||||
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CrateNameParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
|
||||
const PATH: &[Symbol] = &[sym::crate_name];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(n) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(name) = n.value_as_str() else {
|
||||
cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::CrateName {
|
||||
name,
|
||||
name_span: n.value_span,
|
||||
attr_span: cx.attr_span,
|
||||
style: cx.attr_style,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RecursionLimitParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
|
||||
const PATH: &[Symbol] = &[sym::recursion_limit];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::RecursionLimit {
|
||||
limit: cx.parse_limit_int(nv)?,
|
||||
attr_span: cx.attr_span,
|
||||
limit_span: nv.value_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MoveSizeLimitParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
|
||||
const PATH: &[Symbol] = &[sym::move_size_limit];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::MoveSizeLimit {
|
||||
limit: cx.parse_limit_int(nv)?,
|
||||
attr_span: cx.attr_span,
|
||||
limit_span: nv.value_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TypeLengthLimitParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
|
||||
const PATH: &[Symbol] = &[sym::type_length_limit];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::TypeLengthLimit {
|
||||
limit: cx.parse_limit_int(nv)?,
|
||||
attr_span: cx.attr_span,
|
||||
limit_span: nv.value_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PatternComplexityLimitParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
|
||||
const PATH: &[Symbol] = &[sym::pattern_complexity_limit];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::PatternComplexityLimit {
|
||||
limit: cx.parse_limit_int(nv)?,
|
||||
attr_span: cx.attr_span,
|
||||
limit_span: nv.value_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NoCoreParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for NoCoreParser {
|
||||
const PATH: &[Symbol] = &[sym::no_core];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore;
|
||||
}
|
||||
|
||||
pub(crate) struct NoStdParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for NoStdParser {
|
||||
const PATH: &[Symbol] = &[sym::no_std];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcCoherenceIsCoreParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
|
||||
}
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
|
||||
|
||||
use super::prelude::*;
|
||||
use super::util::parse_version;
|
||||
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::{
|
||||
DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince,
|
||||
};
|
||||
|
||||
pub(crate) struct DeprecationParser;
|
||||
|
||||
|
|
@ -38,9 +36,35 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
const PATH: &[Symbol] = &[sym::deprecated];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Const),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Use),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Crate),
|
||||
Error(Target::WherePredicate),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word,
|
||||
List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
|
||||
List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
|
||||
NameValueStr: "reason"
|
||||
);
|
||||
|
||||
|
|
@ -75,7 +99,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
}
|
||||
Some(name @ sym::suggestion) => {
|
||||
if !features.deprecated_suggestion() {
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
cx.emit_err(DeprecatedItemSuggestion {
|
||||
span: param.span(),
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
|
|
@ -117,18 +141,18 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
} else if let Some(version) = parse_version(since) {
|
||||
DeprecatedSince::RustcVersion(version)
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
|
||||
cx.emit_err(InvalidSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
}
|
||||
} else if is_rustc {
|
||||
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
|
||||
cx.emit_err(MissingSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
} else {
|
||||
DeprecatedSince::Unspecified
|
||||
};
|
||||
|
||||
if is_rustc && note.is_none() {
|
||||
cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
|
||||
cx.emit_err(MissingNote { span: cx.attr_span });
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ use rustc_span::{Symbol, sym};
|
|||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
|
||||
|
||||
pub(crate) struct DummyParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for DummyParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_dummy];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
|
||||
|
||||
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
|
|
|
|||
|
|
@ -2,15 +2,9 @@
|
|||
// note: need to model better how duplicate attr errors work when not using
|
||||
// SingleAttributeParser which is what we have two of here.
|
||||
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr};
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::{AcceptContext, AttributeOrder, OnDuplicate};
|
||||
use crate::attributes::SingleAttributeParser;
|
||||
use crate::context::Stage;
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct InlineParser;
|
||||
|
||||
|
|
@ -18,7 +12,26 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
const PATH: &'static [Symbol] = &[sym::inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Delegation { mac: false }),
|
||||
Warn(Target::Method(MethodKind::Trait { body: false })),
|
||||
Warn(Target::ForeignFn),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::MacroDef),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::AssocConst),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word,
|
||||
List: &["always", "never"],
|
||||
"https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args {
|
||||
|
|
@ -37,14 +50,14 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument(l.span(), vec!["always", "never"]);
|
||||
cx.expected_specific_argument(l.span(), &[sym::always, sym::never]);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "inline");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
@ -59,7 +72,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
|||
const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let reason = match args {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_hir::attrs::*;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::kw;
|
||||
use rustc_target::spec::BinaryFormat;
|
||||
|
||||
use crate::attributes::{
|
||||
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
use super::prelude::*;
|
||||
use super::util::parse_single_integer;
|
||||
use crate::attributes::cfg::parse_cfg_entry;
|
||||
use crate::fluent_generated;
|
||||
use crate::session_diagnostics::{
|
||||
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86,
|
||||
IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange,
|
||||
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
|
||||
WholeArchiveNeedsStatic,
|
||||
};
|
||||
use crate::context::{AcceptContext, Stage, parse_single_integer};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
|
||||
|
||||
pub(crate) struct LinkNameParser;
|
||||
|
||||
|
|
@ -16,7 +23,14 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
|
|||
const PATH: &[Symbol] = &[sym::link_name];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
NameValueStr: "name",
|
||||
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
|
|
@ -32,13 +46,421 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinkParser;
|
||||
|
||||
impl<S: Stage> CombineAttributeParser<S> for LinkParser {
|
||||
type Item = LinkEntry;
|
||||
const PATH: &[Symbol] = &[sym::link];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Link;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[
|
||||
r#"name = "...""#,
|
||||
r#"name = "...", kind = "dylib|static|...""#,
|
||||
r#"name = "...", wasm_import_module = "...""#,
|
||||
r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#,
|
||||
r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#,
|
||||
], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
let mut result = None;
|
||||
let Some(items) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return result;
|
||||
};
|
||||
|
||||
let sess = cx.sess();
|
||||
let features = cx.features();
|
||||
|
||||
let mut name = None;
|
||||
let mut kind = None;
|
||||
let mut modifiers = None;
|
||||
let mut cfg = None;
|
||||
let mut wasm_import_module = None;
|
||||
let mut import_name_type = None;
|
||||
for item in items.mixed() {
|
||||
let Some(item) = item.meta_item() else {
|
||||
cx.unexpected_literal(item.span());
|
||||
continue;
|
||||
};
|
||||
|
||||
let cont = match item.path().word().map(|ident| ident.name) {
|
||||
Some(sym::name) => Self::parse_link_name(item, &mut name, cx),
|
||||
Some(sym::kind) => Self::parse_link_kind(item, &mut kind, cx, sess, features),
|
||||
Some(sym::modifiers) => Self::parse_link_modifiers(item, &mut modifiers, cx),
|
||||
Some(sym::cfg) => Self::parse_link_cfg(item, &mut cfg, cx, sess, features),
|
||||
Some(sym::wasm_import_module) => {
|
||||
Self::parse_link_wasm_import_module(item, &mut wasm_import_module, cx)
|
||||
}
|
||||
Some(sym::import_name_type) => {
|
||||
Self::parse_link_import_name_type(item, &mut import_name_type, cx)
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument_strings(
|
||||
item.span(),
|
||||
&[
|
||||
sym::name,
|
||||
sym::kind,
|
||||
sym::modifiers,
|
||||
sym::cfg,
|
||||
sym::wasm_import_module,
|
||||
sym::import_name_type,
|
||||
],
|
||||
);
|
||||
true
|
||||
}
|
||||
};
|
||||
if !cont {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Do this outside the above loop so we don't depend on modifiers coming after kinds
|
||||
let mut verbatim = None;
|
||||
if let Some((modifiers, span)) = modifiers {
|
||||
for modifier in modifiers.as_str().split(',') {
|
||||
let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) {
|
||||
Some(m) => (Symbol::intern(m), modifier.starts_with('+')),
|
||||
None => {
|
||||
cx.emit_err(InvalidLinkModifier { span });
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
macro report_unstable_modifier($feature: ident) {
|
||||
if !features.$feature() {
|
||||
// FIXME: make this translatable
|
||||
#[expect(rustc::untranslatable_diagnostic)]
|
||||
feature_err(
|
||||
sess,
|
||||
sym::$feature,
|
||||
span,
|
||||
format!("linking modifier `{modifier}` is unstable"),
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
let assign_modifier = |dst: &mut Option<bool>| {
|
||||
if dst.is_some() {
|
||||
cx.emit_err(MultipleModifiers { span, modifier });
|
||||
} else {
|
||||
*dst = Some(value);
|
||||
}
|
||||
};
|
||||
match (modifier, &mut kind) {
|
||||
(sym::bundle, Some(NativeLibKind::Static { bundle, .. })) => {
|
||||
assign_modifier(bundle)
|
||||
}
|
||||
(sym::bundle, _) => {
|
||||
cx.emit_err(BundleNeedsStatic { span });
|
||||
}
|
||||
|
||||
(sym::verbatim, _) => assign_modifier(&mut verbatim),
|
||||
|
||||
(
|
||||
sym::whole_dash_archive,
|
||||
Some(NativeLibKind::Static { whole_archive, .. }),
|
||||
) => assign_modifier(whole_archive),
|
||||
(sym::whole_dash_archive, _) => {
|
||||
cx.emit_err(WholeArchiveNeedsStatic { span });
|
||||
}
|
||||
|
||||
(sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed }))
|
||||
| (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => {
|
||||
report_unstable_modifier!(native_link_modifiers_as_needed);
|
||||
assign_modifier(as_needed)
|
||||
}
|
||||
(sym::as_dash_needed, _) => {
|
||||
cx.emit_err(AsNeededCompatibility { span });
|
||||
}
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument_strings(
|
||||
span,
|
||||
&[
|
||||
sym::bundle,
|
||||
sym::verbatim,
|
||||
sym::whole_dash_archive,
|
||||
sym::as_dash_needed,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, span)) = wasm_import_module {
|
||||
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
|
||||
cx.emit_err(IncompatibleWasmLink { span });
|
||||
}
|
||||
}
|
||||
|
||||
if wasm_import_module.is_some() {
|
||||
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
|
||||
}
|
||||
let Some((name, name_span)) = name else {
|
||||
cx.emit_err(LinkRequiresName { span: cx.attr_span });
|
||||
return result;
|
||||
};
|
||||
|
||||
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
|
||||
if let Some((_, span)) = import_name_type {
|
||||
if kind != Some(NativeLibKind::RawDylib) {
|
||||
cx.emit_err(ImportNameTypeRaw { span });
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(NativeLibKind::RawDylib) = kind
|
||||
&& name.as_str().contains('\0')
|
||||
{
|
||||
cx.emit_err(RawDylibNoNul { span: name_span });
|
||||
}
|
||||
|
||||
result = Some(LinkEntry {
|
||||
span: cx.attr_span,
|
||||
kind: kind.unwrap_or(NativeLibKind::Unspecified),
|
||||
name,
|
||||
cfg,
|
||||
verbatim,
|
||||
import_name_type,
|
||||
});
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkParser {
|
||||
fn parse_link_name<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
name: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
if name.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::name);
|
||||
return true;
|
||||
}
|
||||
let Some(nv) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::name));
|
||||
return false;
|
||||
};
|
||||
let Some(link_name) = nv.value_as_str() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::name));
|
||||
return false;
|
||||
};
|
||||
|
||||
if link_name.is_empty() {
|
||||
cx.emit_err(EmptyLinkName { span: nv.value_span });
|
||||
}
|
||||
*name = Some((link_name, nv.value_span));
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_link_kind<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
kind: &mut Option<NativeLibKind>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
) -> bool {
|
||||
if kind.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::kind);
|
||||
return true;
|
||||
}
|
||||
let Some(nv) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::kind));
|
||||
return true;
|
||||
};
|
||||
let Some(link_kind) = nv.value_as_str() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::kind));
|
||||
return true;
|
||||
};
|
||||
|
||||
let link_kind = match link_kind {
|
||||
kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
|
||||
sym::dylib => NativeLibKind::Dylib { as_needed: None },
|
||||
sym::framework => {
|
||||
if !sess.target.is_like_darwin {
|
||||
cx.emit_err(LinkFrameworkApple { span: nv.value_span });
|
||||
}
|
||||
NativeLibKind::Framework { as_needed: None }
|
||||
}
|
||||
sym::raw_dash_dylib => {
|
||||
if sess.target.is_like_windows {
|
||||
// raw-dylib is stable and working on Windows
|
||||
} else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
|
||||
{
|
||||
// raw-dylib is unstable on ELF, but the user opted in
|
||||
} else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build()
|
||||
{
|
||||
feature_err(
|
||||
sess,
|
||||
sym::raw_dylib_elf,
|
||||
nv.value_span,
|
||||
fluent_generated::attr_parsing_raw_dylib_elf_unstable,
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
|
||||
}
|
||||
|
||||
NativeLibKind::RawDylib
|
||||
}
|
||||
sym::link_dash_arg => {
|
||||
if !features.link_arg_attribute() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::link_arg_attribute,
|
||||
nv.value_span,
|
||||
fluent_generated::attr_parsing_link_arg_unstable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
NativeLibKind::LinkArg
|
||||
}
|
||||
_kind => {
|
||||
cx.expected_specific_argument_strings(
|
||||
nv.value_span,
|
||||
&[
|
||||
kw::Static,
|
||||
sym::dylib,
|
||||
sym::framework,
|
||||
sym::raw_dash_dylib,
|
||||
sym::link_dash_arg,
|
||||
],
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
*kind = Some(link_kind);
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_link_modifiers<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
modifiers: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
if modifiers.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::modifiers);
|
||||
return true;
|
||||
}
|
||||
let Some(nv) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::modifiers));
|
||||
return true;
|
||||
};
|
||||
let Some(link_modifiers) = nv.value_as_str() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::modifiers));
|
||||
return true;
|
||||
};
|
||||
*modifiers = Some((link_modifiers, nv.value_span));
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_link_cfg<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
cfg: &mut Option<CfgEntry>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
) -> bool {
|
||||
if cfg.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::cfg);
|
||||
return true;
|
||||
}
|
||||
let Some(link_cfg) = item.args().list() else {
|
||||
cx.expected_list(item.span());
|
||||
return true;
|
||||
};
|
||||
let Some(link_cfg) = link_cfg.single() else {
|
||||
cx.expected_single_argument(item.span());
|
||||
return true;
|
||||
};
|
||||
if !features.link_cfg() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::link_cfg,
|
||||
item.span(),
|
||||
fluent_generated::attr_parsing_link_cfg_unstable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
*cfg = parse_cfg_entry(cx, link_cfg);
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_link_wasm_import_module<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
wasm_import_module: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
if wasm_import_module.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::wasm_import_module);
|
||||
return true;
|
||||
}
|
||||
let Some(nv) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::wasm_import_module));
|
||||
return true;
|
||||
};
|
||||
let Some(link_wasm_import_module) = nv.value_as_str() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::wasm_import_module));
|
||||
return true;
|
||||
};
|
||||
*wasm_import_module = Some((link_wasm_import_module, item.span()));
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_link_import_name_type<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
import_name_type: &mut Option<(PeImportNameType, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
if import_name_type.is_some() {
|
||||
cx.duplicate_key(item.span(), sym::import_name_type);
|
||||
return true;
|
||||
}
|
||||
let Some(nv) = item.args().name_value() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::import_name_type));
|
||||
return true;
|
||||
};
|
||||
let Some(link_import_name_type) = nv.value_as_str() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::import_name_type));
|
||||
return true;
|
||||
};
|
||||
if cx.sess().target.arch != "x86" {
|
||||
cx.emit_err(ImportNameTypeX86 { span: item.span() });
|
||||
return true;
|
||||
}
|
||||
|
||||
let link_import_name_type = match link_import_name_type {
|
||||
sym::decorated => PeImportNameType::Decorated,
|
||||
sym::noprefix => PeImportNameType::NoPrefix,
|
||||
sym::undecorated => PeImportNameType::Undecorated,
|
||||
_ => {
|
||||
cx.expected_specific_argument_strings(
|
||||
item.span(),
|
||||
&[sym::decorated, sym::noprefix, sym::undecorated],
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
*import_name_type = Some((link_import_name_type, item.span()));
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinkSectionParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
|
||||
const PATH: &[Symbol] = &[sym::link_section];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
NameValueStr: "name",
|
||||
"https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
|
|
@ -64,6 +486,7 @@ pub(crate) struct ExportStableParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ExportStableParser {
|
||||
const PATH: &[Symbol] = &[sym::export_stable];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +494,7 @@ pub(crate) struct FfiConstParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser {
|
||||
const PATH: &[Symbol] = &[sym::ffi_const];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst;
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +502,7 @@ pub(crate) struct FfiPureParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser {
|
||||
const PATH: &[Symbol] = &[sym::ffi_pure];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +510,12 @@ pub(crate) struct StdInternalSymbolParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::ForeignStatic),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +525,15 @@ impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
|
|||
const PATH: &[Symbol] = &[sym::link_ordinal];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "ordinal");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
List: &["ordinal"],
|
||||
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let ordinal = parse_single_integer(cx, args)?;
|
||||
|
|
@ -120,3 +559,87 @@ impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
|
|||
Some(LinkOrdinal { ordinal, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinkageParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
|
||||
const PATH: &[Symbol] = &[sym::linkage];
|
||||
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignFn),
|
||||
]);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
|
||||
"available_externally",
|
||||
"common",
|
||||
"extern_weak",
|
||||
"external",
|
||||
"internal",
|
||||
"linkonce",
|
||||
"linkonce_odr",
|
||||
"weak",
|
||||
"weak_odr",
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(name_value) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, Some(sym::linkage));
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(value) = name_value.value_as_str() else {
|
||||
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
// Use the names from src/llvm/docs/LangRef.rst here. Most types are only
|
||||
// applicable to variable declarations and may not really make sense for
|
||||
// Rust code in the first place but allow them anyway and trust that the
|
||||
// user knows what they're doing. Who knows, unanticipated use cases may pop
|
||||
// up in the future.
|
||||
//
|
||||
// ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
|
||||
// and don't have to be, LLVM treats them as no-ops.
|
||||
let linkage = match value {
|
||||
sym::available_externally => Linkage::AvailableExternally,
|
||||
sym::common => Linkage::Common,
|
||||
sym::extern_weak => Linkage::ExternalWeak,
|
||||
sym::external => Linkage::External,
|
||||
sym::internal => Linkage::Internal,
|
||||
sym::linkonce => Linkage::LinkOnceAny,
|
||||
sym::linkonce_odr => Linkage::LinkOnceODR,
|
||||
sym::weak => Linkage::WeakAny,
|
||||
sym::weak_odr => Linkage::WeakODR,
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument(
|
||||
name_value.value_span,
|
||||
&[
|
||||
sym::available_externally,
|
||||
sym::common,
|
||||
sym::extern_weak,
|
||||
sym::external,
|
||||
sym::internal,
|
||||
sym::linkonce,
|
||||
sym::linkonce_odr,
|
||||
sym::weak,
|
||||
sym::weak_odr,
|
||||
],
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::Linkage(linkage, cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct AsPtrParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_as_ptr];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr;
|
||||
}
|
||||
|
||||
|
|
@ -15,6 +18,11 @@ pub(crate) struct PubTransparentParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for PubTransparentParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_pub_transparent];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Union),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent;
|
||||
}
|
||||
|
||||
|
|
@ -22,6 +30,11 @@ pub(crate) struct PassByValueParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_pass_by_value];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::TyAlias),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
|
||||
}
|
||||
|
||||
|
|
@ -29,5 +42,10 @@ pub(crate) struct AutomaticallyDerivedParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for AutomaticallyDerivedParser {
|
||||
const PATH: &[Symbol] = &[sym::automatically_derived];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Error(Target::Crate),
|
||||
Error(Target::WherePredicate),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AutomaticallyDerived;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct LoopMatchParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for LoopMatchParser {
|
||||
const PATH: &[Symbol] = &[sym::loop_match];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Expression)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::LoopMatch;
|
||||
}
|
||||
|
||||
|
|
@ -15,5 +12,6 @@ pub(crate) struct ConstContinueParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ConstContinueParser {
|
||||
const PATH: &[Symbol] = &[sym::const_continue];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Expression)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstContinue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
use rustc_ast::AttrStyle;
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::{AttributeKind, MacroUseArgs};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
use rustc_hir::attrs::MacroUseArgs;
|
||||
|
||||
use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::IllFormedAttributeInputLint;
|
||||
|
||||
pub(crate) struct MacroEscapeParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
|
||||
const PATH: &[Symbol] = &[sym::macro_escape];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +28,16 @@ pub(crate) struct MacroUseParser {
|
|||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
|
||||
const MACRO_USE_TEMPLATE: AttributeTemplate = template!(
|
||||
Word, List: &["name1, name2, ..."],
|
||||
"https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
|
||||
);
|
||||
const MACRO_USE_ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::ExternCrate),
|
||||
Allow(Target::Crate),
|
||||
Error(Target::WherePredicate),
|
||||
]);
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
|
|
@ -96,8 +102,8 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
|||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
|
|
@ -108,8 +114,85 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
|||
}
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct AllowInternalUnsafeParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser {
|
||||
const PATH: &[Symbol] = &[sym::allow_internal_unsafe];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::MacroDef),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
|
||||
}
|
||||
|
||||
pub(crate) struct MacroExportParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
|
||||
const PATH: &[Symbol] = &[sym::macro_export];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]);
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::MacroDef),
|
||||
Error(Target::WherePredicate),
|
||||
Error(Target::Crate),
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let suggestions = || {
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(AttrStyle::Inner, "macro_export")
|
||||
};
|
||||
let local_inner_macros = match args {
|
||||
ArgParser::NoArgs => false,
|
||||
ArgParser::List(list) => {
|
||||
let Some(l) = list.single() else {
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::InvalidMacroExportArguments {
|
||||
suggestions: suggestions(),
|
||||
},
|
||||
span,
|
||||
);
|
||||
return None;
|
||||
};
|
||||
match l.meta_item().and_then(|i| i.path().word_sym()) {
|
||||
Some(sym::local_inner_macros) => true,
|
||||
_ => {
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::InvalidMacroExportArguments {
|
||||
suggestions: suggestions(),
|
||||
},
|
||||
span,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let span = cx.attr_span;
|
||||
let suggestions = suggestions();
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
),
|
||||
span,
|
||||
});
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
//! Specifically, you might not care about managing the state of your [`AttributeParser`]
|
||||
//! state machine yourself. In this case you can choose to implement:
|
||||
//!
|
||||
//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
|
||||
//! - [`SingleAttributeParser`](crate::attributes::SingleAttributeParser): makes it easy to implement an attribute which should error if it
|
||||
//! appears more than once in a list of attributes
|
||||
//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
|
||||
//! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the
|
||||
//! contents of attributes, if an attribute appear multiple times in a list
|
||||
//!
|
||||
//! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed.
|
||||
|
|
@ -24,6 +24,10 @@ use thin_vec::ThinVec;
|
|||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics::UnusedMultiple;
|
||||
use crate::target_checking::AllowedTargets;
|
||||
|
||||
/// All the parsers require roughly the same imports, so this prelude has most of the often-needed ones.
|
||||
mod prelude;
|
||||
|
||||
pub(crate) mod allow_unstable;
|
||||
pub(crate) mod body;
|
||||
|
|
@ -31,6 +35,7 @@ pub(crate) mod cfg;
|
|||
pub(crate) mod cfg_old;
|
||||
pub(crate) mod codegen_attrs;
|
||||
pub(crate) mod confusables;
|
||||
pub(crate) mod crate_level;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod dummy;
|
||||
pub(crate) mod inline;
|
||||
|
|
@ -43,6 +48,7 @@ pub(crate) mod no_implicit_prelude;
|
|||
pub(crate) mod non_exhaustive;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod proc_macro_attrs;
|
||||
pub(crate) mod prototype;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod rustc_internal;
|
||||
pub(crate) mod semantics;
|
||||
|
|
@ -79,6 +85,7 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
|
|||
///
|
||||
/// If an attribute has this symbol, the `accept` function will be called on it.
|
||||
const ATTRIBUTES: AcceptMapping<Self, S>;
|
||||
const ALLOWED_TARGETS: AllowedTargets;
|
||||
|
||||
/// The parser has gotten a chance to accept the attributes on an item,
|
||||
/// here it can produce an attribute.
|
||||
|
|
@ -116,6 +123,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
|
|||
/// and this specified whether to, for example, warn or error on the other one.
|
||||
const ON_DUPLICATE: OnDuplicate<S>;
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets;
|
||||
|
||||
/// The template this attribute parser should implement. Used for diagnostics.
|
||||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
|
|
@ -163,6 +172,7 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>
|
|||
}
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(self.1?.0)
|
||||
|
|
@ -247,6 +257,7 @@ pub(crate) enum AttributeOrder {
|
|||
pub(crate) trait NoArgsAttributeParser<S: Stage>: 'static {
|
||||
const PATH: &[Symbol];
|
||||
const ON_DUPLICATE: OnDuplicate<S>;
|
||||
const ALLOWED_TARGETS: AllowedTargets;
|
||||
|
||||
/// Create the [`AttributeKind`] given attribute's [`Span`].
|
||||
const CREATE: fn(Span) -> AttributeKind;
|
||||
|
|
@ -264,6 +275,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without
|
|||
const PATH: &[Symbol] = T::PATH;
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = T::ON_DUPLICATE;
|
||||
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
|
|
@ -293,6 +305,8 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
|
|||
/// where `x` is a vec of these individual reprs.
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets;
|
||||
|
||||
/// The template this attribute parser should implement. Used for diagnostics.
|
||||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
|
|
@ -324,15 +338,13 @@ impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
|
|||
}
|
||||
|
||||
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
T::PATH,
|
||||
<T as CombineAttributeParser<S>>::TEMPLATE,
|
||||
|group: &mut Combine<T, S>, cx, args| {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(T::PATH, T::TEMPLATE, |group: &mut Combine<T, S>, cx, args| {
|
||||
// Keep track of the span of the first attribute, for diagnostics
|
||||
group.first_span.get_or_insert(cx.attr_span);
|
||||
group.items.extend(T::extend(cx, args))
|
||||
},
|
||||
)];
|
||||
})];
|
||||
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(first_span) = self.first_span {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::IllFormedAttributeInputLint;
|
||||
|
||||
pub(crate) struct MustUseParser;
|
||||
|
||||
|
|
@ -14,7 +9,25 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
const PATH: &[Symbol] = &[sym::must_use];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::ForeignFn),
|
||||
// `impl Trait` in return position can trip
|
||||
// `unused_must_use` if `Trait` is marked as
|
||||
// `#[must_use]`
|
||||
Allow(Target::Trait),
|
||||
Error(Target::WherePredicate),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word, NameValueStr: "reason",
|
||||
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MustUse {
|
||||
|
|
@ -32,9 +45,9 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
Some(value_str)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "must_use");
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct NoImplicitPreludeParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for NoImplicitPreludeParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::no_implicit_prelude];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowListWarnRest(&[Allow(Target::Mod), Allow(Target::Crate)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoImplicitPrelude;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use crate::target_checking::AllowedTargets;
|
||||
use crate::target_checking::Policy::{Allow, Warn};
|
||||
|
||||
pub(crate) struct NonExhaustiveParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for NonExhaustiveParser {
|
||||
const PATH: &[Symbol] = &[sym::non_exhaustive];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Variant),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::MacroDef),
|
||||
Warn(Target::MacroCall),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NonExhaustive;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct PathParser;
|
||||
|
||||
|
|
@ -12,7 +6,12 @@ impl<S: Stage> SingleAttributeParser<S> for PathParser {
|
|||
const PATH: &[Symbol] = &[sym::path];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "file");
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowListWarnRest(&[Allow(Target::Mod), Error(Target::Crate)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
NameValueStr: "file",
|
||||
"https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
|
|
|
|||
29
compiler/rustc_attr_parsing/src/attributes/prelude.rs
Normal file
29
compiler/rustc_attr_parsing/src/attributes/prelude.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// data structures
|
||||
#[doc(hidden)]
|
||||
pub(super) use rustc_feature::{AttributeTemplate, template};
|
||||
#[doc(hidden)]
|
||||
pub(super) use rustc_hir::attrs::AttributeKind;
|
||||
#[doc(hidden)]
|
||||
pub(super) use rustc_hir::lints::AttributeLintKind;
|
||||
#[doc(hidden)]
|
||||
pub(super) use rustc_hir::{MethodKind, Target};
|
||||
#[doc(hidden)]
|
||||
pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
|
||||
#[doc(hidden)]
|
||||
pub(super) use thin_vec::ThinVec;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) use crate::attributes::{
|
||||
AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
|
||||
NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
// contexts
|
||||
#[doc(hidden)]
|
||||
pub(super) use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
#[doc(hidden)]
|
||||
pub(super) use crate::parser::*;
|
||||
// target checking
|
||||
#[doc(hidden)]
|
||||
pub(super) use crate::target_checking::Policy::{Allow, Error, Warn};
|
||||
#[doc(hidden)]
|
||||
pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets};
|
||||
|
|
@ -1,18 +1,13 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
use super::prelude::*;
|
||||
|
||||
use crate::attributes::{
|
||||
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Fn), Warn(Target::Crate), Warn(Target::MacroCall)]);
|
||||
|
||||
pub(crate) struct ProcMacroParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro;
|
||||
}
|
||||
|
||||
|
|
@ -20,6 +15,7 @@ pub(crate) struct ProcMacroAttributeParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroAttributeParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro_attribute];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute;
|
||||
}
|
||||
|
||||
|
|
@ -28,8 +24,11 @@ impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
|
|||
const PATH: &[Symbol] = &[sym::proc_macro_derive];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS;
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
List: &["TraitName", "TraitName, attributes(name1, name2, ...)"],
|
||||
"https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
|
||||
|
|
@ -46,8 +45,9 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
|
|||
const PATH: &[Symbol] = &[sym::rustc_builtin_macro];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
|
||||
|
|
@ -100,7 +100,7 @@ fn parse_derive_like<S: Stage>(
|
|||
return None;
|
||||
};
|
||||
if !attr_list.path().word_is(sym::attributes) {
|
||||
cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
|
||||
cx.expected_specific_argument(attrs.span(), &[sym::attributes]);
|
||||
return None;
|
||||
}
|
||||
let Some(attr_list) = attr_list.args().list() else {
|
||||
|
|
|
|||
141
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal file
141
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//! Attributes that are only used on function prototypes.
|
||||
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{AttributeOrder, OnDuplicate};
|
||||
use crate::attributes::SingleAttributeParser;
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::target_checking::AllowedTargets;
|
||||
use crate::target_checking::Policy::Allow;
|
||||
|
||||
pub(crate) struct CustomMirParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
|
||||
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut dialect = None;
|
||||
let mut phase = None;
|
||||
let mut failed = false;
|
||||
|
||||
for item in list.mixed() {
|
||||
let Some(meta_item) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), None);
|
||||
failed = true;
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(arg) = meta_item.word_is(sym::dialect) {
|
||||
extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
|
||||
} else if let Some(arg) = meta_item.word_is(sym::phase) {
|
||||
extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
|
||||
} else if let Some(word) = meta_item.path().word() {
|
||||
let word = word.to_string();
|
||||
cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
|
||||
failed = true;
|
||||
} else {
|
||||
cx.expected_name_value(meta_item.span(), None);
|
||||
failed = true;
|
||||
};
|
||||
}
|
||||
|
||||
let dialect = parse_dialect(cx, dialect, &mut failed);
|
||||
let phase = parse_phase(cx, phase, &mut failed);
|
||||
|
||||
if failed {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_value<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
key: Symbol,
|
||||
arg: &ArgParser<'_>,
|
||||
span: Span,
|
||||
out_val: &mut Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) {
|
||||
if out_val.is_some() {
|
||||
cx.duplicate_key(span, key);
|
||||
*failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(val) = arg.name_value() else {
|
||||
cx.expected_single_argument(arg.span().unwrap_or(span));
|
||||
*failed = true;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(value_sym) = val.value_as_str() else {
|
||||
cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
|
||||
*failed = true;
|
||||
return;
|
||||
};
|
||||
|
||||
*out_val = Some((value_sym, val.value_span));
|
||||
}
|
||||
|
||||
fn parse_dialect<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
dialect: Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) -> Option<(MirDialect, Span)> {
|
||||
let (dialect, span) = dialect?;
|
||||
|
||||
let dialect = match dialect {
|
||||
sym::analysis => MirDialect::Analysis,
|
||||
sym::built => MirDialect::Built,
|
||||
sym::runtime => MirDialect::Runtime,
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
|
||||
*failed = true;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((dialect, span))
|
||||
}
|
||||
|
||||
fn parse_phase<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
phase: Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) -> Option<(MirPhase, Span)> {
|
||||
let (phase, span) = phase?;
|
||||
|
||||
let phase = match phase {
|
||||
sym::initial => MirPhase::Initial,
|
||||
sym::post_cleanup => MirPhase::PostCleanup,
|
||||
sym::optimized => MirPhase::Optimized,
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument(span, &[sym::initial, sym::post_cleanup, sym::optimized]);
|
||||
*failed = true;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((phase, span))
|
||||
}
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
use rustc_abi::Align;
|
||||
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::{AttributeKind, IntType, ReprAttr};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
use rustc_hir::attrs::{IntType, ReprAttr};
|
||||
|
||||
use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
|
||||
use super::prelude::*;
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
|
||||
/// Parse #[repr(...)] forms.
|
||||
///
|
||||
|
|
@ -26,8 +21,10 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
const CONVERT: ConvertFn<Self::Item> =
|
||||
|items, first_span| AttributeKind::Repr { reprs: items, first_span };
|
||||
// FIXME(jdonszelmann): never used
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
|
||||
"https://doc.rust-lang.org/reference/type-layout.html#representations"
|
||||
);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
|
|
@ -58,6 +55,10 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
|
||||
reprs
|
||||
}
|
||||
|
||||
//FIXME Still checked fully in `check_attr.rs`
|
||||
//This one is slightly more complicated because the allowed targets depend on the arguments
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
}
|
||||
|
||||
macro_rules! int_pat {
|
||||
|
|
@ -275,7 +276,7 @@ pub(crate) struct AlignParser(Option<(Align, Span)>);
|
|||
|
||||
impl AlignParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_align];
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "<alignment in bytes>");
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
|
|
@ -316,9 +317,44 @@ impl AlignParser {
|
|||
|
||||
impl<S: Stage> AttributeParser<S> for AlignParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::ForeignFn),
|
||||
]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (align, span) = self.0?;
|
||||
Some(AttributeKind::Align { align, span })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct AlignStaticParser(AlignParser);
|
||||
|
||||
impl AlignStaticParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
|
||||
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) {
|
||||
self.0.parse(cx, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for AlignStaticParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (align, span) = self.0.0?;
|
||||
Some(AttributeKind::Align { align, span })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage, parse_single_integer};
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
use super::util::parse_single_integer;
|
||||
|
||||
pub(crate) struct RustcLayoutScalarValidRangeStart;
|
||||
|
||||
|
|
@ -12,7 +7,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
|
|||
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "start");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["start"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
parse_single_integer(cx, args)
|
||||
|
|
@ -26,7 +22,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
|
|||
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "end");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["end"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
parse_single_integer(cx, args)
|
||||
|
|
@ -40,6 +37,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
|
|||
const PATH: &[rustc_span::Symbol] = &[sym::rustc_object_lifetime_default];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct MayDangleParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for MayDangleParser {
|
||||
const PATH: &[Symbol] = &[sym::may_dangle];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,13 @@
|
|||
use std::num::NonZero;
|
||||
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_feature::template;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::{
|
||||
DefaultBodyStability, PartialConstStability, Stability, StabilityLevel, StableSince,
|
||||
UnstableReason, VERSION_PLACEHOLDER,
|
||||
DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
|
||||
StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
|
||||
};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
|
||||
use super::prelude::*;
|
||||
use super::util::parse_version;
|
||||
use super::{AcceptMapping, AttributeParser, OnDuplicate};
|
||||
use crate::attributes::NoArgsAttributeParser;
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
|
||||
macro_rules! reject_outside_std {
|
||||
|
|
@ -26,6 +20,36 @@ macro_rules! reject_outside_std {
|
|||
};
|
||||
}
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Crate),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::Use), // FIXME I don't think this does anything?
|
||||
Allow(Target::Const),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::TraitAlias),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Param),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ExternCrate),
|
||||
]);
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StabilityParser {
|
||||
allowed_through_unstable_modules: Option<Symbol>,
|
||||
|
|
@ -48,7 +72,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
|
|||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(
|
||||
&[sym::stable],
|
||||
template!(List: r#"feature = "name", since = "version""#),
|
||||
template!(List: &[r#"feature = "name", since = "version""#]),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
|
|
@ -60,7 +84,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
|
|||
),
|
||||
(
|
||||
&[sym::unstable],
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|
||||
template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
|
|
@ -87,6 +111,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
|
|||
},
|
||||
),
|
||||
];
|
||||
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(atum) = self.allowed_through_unstable_modules {
|
||||
|
|
@ -131,7 +156,7 @@ pub(crate) struct BodyStabilityParser {
|
|||
impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::rustc_default_body_unstable],
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|
||||
template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if this.stability.is_some() {
|
||||
|
|
@ -142,6 +167,7 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
|
|||
}
|
||||
},
|
||||
)];
|
||||
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (stability, span) = self.stability?;
|
||||
|
|
@ -154,6 +180,10 @@ pub(crate) struct ConstStabilityIndirectParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ConstStabilityIndirectParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect;
|
||||
}
|
||||
|
||||
|
|
@ -177,34 +207,43 @@ impl ConstStabilityParser {
|
|||
|
||||
impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
(
|
||||
&[sym::rustc_const_stable],
|
||||
template!(List: &[r#"feature = "name""#]),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
(
|
||||
&[sym::rustc_const_unstable],
|
||||
template!(List: &[r#"feature = "name""#]),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
(&[sym::rustc_promotable], template!(Word), |this, cx, _| {
|
||||
reject_outside_std!(cx);
|
||||
this.promotable = true;
|
||||
}),
|
||||
];
|
||||
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.promotable {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,4 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct IgnoreParser;
|
||||
|
||||
|
|
@ -13,7 +6,12 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
const PATH: &[Symbol] = &[sym::ignore];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word, NameValueStr: "reason",
|
||||
"https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::Ignore {
|
||||
|
|
@ -23,7 +21,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
ArgParser::NameValue(name_value) => {
|
||||
let Some(str_value) = name_value.value_as_str() else {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(false, "ignore");
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions },
|
||||
|
|
@ -34,8 +32,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
Some(str_value)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
@ -51,8 +49,12 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
|
|||
const PATH: &[Symbol] = &[sym::should_panic];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason");
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word, List: &[r#"expected = "reason""#], NameValueStr: "reason",
|
||||
"https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ShouldPanic {
|
||||
|
|
@ -79,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
|
|||
return None;
|
||||
};
|
||||
if !single.path().word_is(sym::expected) {
|
||||
cx.expected_specific_argument_strings(list.span, vec!["expected"]);
|
||||
cx.expected_specific_argument_strings(list.span, &[sym::expected]);
|
||||
return None;
|
||||
}
|
||||
let Some(nv) = single.args().name_value() else {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
use core::mem;
|
||||
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use std::mem;
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::attributes::{
|
||||
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::target_checking::Policy::{Allow, Warn};
|
||||
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
|
||||
|
||||
pub(crate) struct SkipDuringMethodDispatchParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice");
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let mut array = false;
|
||||
|
|
@ -42,7 +42,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
|
|||
Some(key @ sym::array) => (key, &mut array),
|
||||
Some(key @ sym::boxed_slice) => (key, &mut boxed_slice),
|
||||
_ => {
|
||||
cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]);
|
||||
cx.expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
|
@ -58,6 +58,7 @@ pub(crate) struct ParenSugarParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_paren_sugar];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ pub(crate) struct TypeConstParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for TypeConstParser {
|
||||
const PATH: &[Symbol] = &[sym::type_const];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocConst)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst;
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +76,12 @@ pub(crate) struct MarkerParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for MarkerParser {
|
||||
const PATH: &[Symbol] = &[sym::marker];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Trait),
|
||||
Warn(Target::Field),
|
||||
Warn(Target::Arm),
|
||||
Warn(Target::MacroDef),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Marker;
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +89,7 @@ pub(crate) struct DenyExplicitImplParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +97,7 @@ pub(crate) struct DoNotImplementViaObjectParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +108,7 @@ pub(crate) struct ConstTraitParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for ConstTraitParser {
|
||||
const PATH: &[Symbol] = &[sym::const_trait];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstTrait;
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +118,7 @@ pub(crate) struct SpecializationTraitParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for SpecializationTraitParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_specialization_trait];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +126,7 @@ pub(crate) struct UnsafeSpecializationMarkerParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for UnsafeSpecializationMarkerParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +136,7 @@ pub(crate) struct CoinductiveParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for CoinductiveParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_coinductive];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
|
||||
}
|
||||
|
||||
|
|
@ -130,20 +144,17 @@ pub(crate) struct AllowIncoherentImplParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
|
||||
}
|
||||
|
||||
pub(crate) struct CoherenceIsCoreParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore;
|
||||
}
|
||||
|
||||
pub(crate) struct FundamentalParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for FundamentalParser {
|
||||
const PATH: &[Symbol] = &[sym::fundamental];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Struct), Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Fundamental;
|
||||
}
|
||||
|
||||
|
|
@ -151,5 +162,6 @@ pub(crate) struct PointeeParser;
|
|||
impl<S: Stage> NoArgsAttributeParser<S> for PointeeParser {
|
||||
const PATH: &[Symbol] = &[sym::pointee];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Pointee;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use super::prelude::*;
|
||||
|
||||
pub(crate) struct TransparencyParser;
|
||||
|
||||
|
|
@ -18,8 +13,9 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
|
|||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
|
||||
cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
|
||||
});
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(NameValueStr: "transparent|semitransparent|opaque");
|
||||
template!(NameValueStr: ["transparent", "semitransparent", "opaque"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
|
|
@ -33,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
|
|||
Some(_) => {
|
||||
cx.expected_specific_argument_strings(
|
||||
nv.value_span,
|
||||
vec!["transparent", "semitransparent", "opaque"],
|
||||
&[sym::transparent, sym::semitransparent, sym::opaque],
|
||||
);
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_feature::is_builtin_attr_name;
|
||||
use rustc_hir::RustcVersion;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
/// Parse a rustc version number written inside string literal in an attribute,
|
||||
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
|
||||
/// not accepted in this position, unlike when parsing CFG_RELEASE.
|
||||
|
|
@ -23,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
|||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
||||
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
|
||||
attrs: impl Iterator<Item = &'tcx T>,
|
||||
symbol: Symbol,
|
||||
|
|
@ -56,3 +56,32 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Parse a single integer.
|
||||
///
|
||||
/// Used by attributes that take a single integer as argument, such as
|
||||
/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
|
||||
/// `cx` is the context given to the attribute.
|
||||
/// `args` is the parser for the attribute arguments.
|
||||
pub(crate) fn parse_single_integer<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<u128> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
let Some(single) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
let Some(lit) = single.lit() else {
|
||||
cx.expected_integer_literal(single.span());
|
||||
return None;
|
||||
};
|
||||
let LitKind::Int(num, _ty) = lit.kind else {
|
||||
cx.expected_integer_literal(single.span());
|
||||
return None;
|
||||
};
|
||||
Some(num.0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,36 +4,44 @@ use std::ops::{Deref, DerefMut};
|
|||
use std::sync::LazyLock;
|
||||
|
||||
use private::Sealed;
|
||||
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId};
|
||||
use rustc_errors::{Diag, Diagnostic, Level};
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
|
||||
use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
|
||||
use crate::AttributeParser;
|
||||
use crate::attributes::allow_unstable::{
|
||||
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
|
||||
};
|
||||
use crate::attributes::body::CoroutineParser;
|
||||
use crate::attributes::codegen_attrs::{
|
||||
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
|
||||
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
|
||||
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
|
||||
TargetFeatureParser, TrackCallerParser, UsedParser,
|
||||
};
|
||||
use crate::attributes::confusables::ConfusablesParser;
|
||||
use crate::attributes::crate_level::{
|
||||
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
|
||||
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
|
||||
};
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::dummy::DummyParser;
|
||||
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
|
||||
use crate::attributes::link_attrs::{
|
||||
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
|
||||
LinkSectionParser, StdInternalSymbolParser,
|
||||
LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser,
|
||||
};
|
||||
use crate::attributes::lint_helpers::{
|
||||
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
|
||||
};
|
||||
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
|
||||
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
|
||||
use crate::attributes::macro_attrs::{
|
||||
AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser,
|
||||
};
|
||||
use crate::attributes::must_use::MustUseParser;
|
||||
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
|
||||
use crate::attributes::non_exhaustive::NonExhaustiveParser;
|
||||
|
|
@ -41,7 +49,8 @@ use crate::attributes::path::PathParser as PathAttributeParser;
|
|||
use crate::attributes::proc_macro_attrs::{
|
||||
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
|
||||
};
|
||||
use crate::attributes::repr::{AlignParser, ReprParser};
|
||||
use crate::attributes::prototype::CustomMirParser;
|
||||
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
|
||||
use crate::attributes::rustc_internal::{
|
||||
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
|
||||
RustcObjectLifetimeDefaultParser,
|
||||
|
|
@ -52,26 +61,28 @@ use crate::attributes::stability::{
|
|||
};
|
||||
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
|
||||
use crate::attributes::traits::{
|
||||
AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser,
|
||||
DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser,
|
||||
ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser,
|
||||
TypeConstParser, UnsafeSpecializationMarkerParser,
|
||||
AllowIncoherentImplParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser,
|
||||
DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser,
|
||||
PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
|
||||
UnsafeSpecializationMarkerParser,
|
||||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
|
||||
use crate::parser::{ArgParser, MetaItemParser, PathParser};
|
||||
use crate::parser::{ArgParser, PathParser};
|
||||
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
|
||||
use crate::target_checking::AllowedTargets;
|
||||
|
||||
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
|
||||
|
||||
struct GroupTypeInner<S: Stage> {
|
||||
accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
|
||||
finalizers: Vec<FinalizeFn<S>>,
|
||||
pub(super) struct GroupTypeInner<S: Stage> {
|
||||
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
|
||||
pub(super) finalizers: Vec<FinalizeFn<S>>,
|
||||
}
|
||||
|
||||
struct GroupTypeInnerAccept<S: Stage> {
|
||||
template: AttributeTemplate,
|
||||
accept_fn: AcceptFn<S>,
|
||||
pub(super) struct GroupTypeInnerAccept<S: Stage> {
|
||||
pub(super) template: AttributeTemplate,
|
||||
pub(super) accept_fn: AcceptFn<S>,
|
||||
pub(super) allowed_targets: AllowedTargets,
|
||||
}
|
||||
|
||||
type AcceptFn<S> =
|
||||
|
|
@ -119,7 +130,8 @@ macro_rules! attribute_parsers {
|
|||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
accept_fn(s, cx, args)
|
||||
})
|
||||
})
|
||||
}),
|
||||
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +150,7 @@ attribute_parsers!(
|
|||
pub(crate) static ATTRIBUTE_PARSERS = [
|
||||
// tidy-alphabetical-start
|
||||
AlignParser,
|
||||
AlignStaticParser,
|
||||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
|
|
@ -150,6 +163,8 @@ attribute_parsers!(
|
|||
// tidy-alphabetical-start
|
||||
Combine<AllowConstFnUnstableParser>,
|
||||
Combine<AllowInternalUnstableParser>,
|
||||
Combine<ForceTargetFeatureParser>,
|
||||
Combine<LinkParser>,
|
||||
Combine<ReprParser>,
|
||||
Combine<TargetFeatureParser>,
|
||||
Combine<UnstableFeatureBoundParser>,
|
||||
|
|
@ -157,6 +172,8 @@ attribute_parsers!(
|
|||
|
||||
// tidy-alphabetical-start
|
||||
Single<CoverageParser>,
|
||||
Single<CrateNameParser>,
|
||||
Single<CustomMirParser>,
|
||||
Single<DeprecationParser>,
|
||||
Single<DummyParser>,
|
||||
Single<ExportNameParser>,
|
||||
|
|
@ -165,22 +182,31 @@ attribute_parsers!(
|
|||
Single<LinkNameParser>,
|
||||
Single<LinkOrdinalParser>,
|
||||
Single<LinkSectionParser>,
|
||||
Single<LinkageParser>,
|
||||
Single<MacroExportParser>,
|
||||
Single<MoveSizeLimitParser>,
|
||||
Single<MustUseParser>,
|
||||
Single<ObjcClassParser>,
|
||||
Single<ObjcSelectorParser>,
|
||||
Single<OptimizeParser>,
|
||||
Single<PathAttributeParser>,
|
||||
Single<PatternComplexityLimitParser>,
|
||||
Single<ProcMacroDeriveParser>,
|
||||
Single<RecursionLimitParser>,
|
||||
Single<RustcBuiltinMacroParser>,
|
||||
Single<RustcForceInlineParser>,
|
||||
Single<RustcLayoutScalarValidRangeEnd>,
|
||||
Single<RustcLayoutScalarValidRangeStart>,
|
||||
Single<RustcObjectLifetimeDefaultParser>,
|
||||
Single<SanitizeParser>,
|
||||
Single<ShouldPanicParser>,
|
||||
Single<SkipDuringMethodDispatchParser>,
|
||||
Single<TransparencyParser>,
|
||||
Single<TypeLengthLimitParser>,
|
||||
Single<WithoutArgs<AllowIncoherentImplParser>>,
|
||||
Single<WithoutArgs<AllowInternalUnsafeParser>>,
|
||||
Single<WithoutArgs<AsPtrParser>>,
|
||||
Single<WithoutArgs<AutomaticallyDerivedParser>>,
|
||||
Single<WithoutArgs<CoherenceIsCoreParser>>,
|
||||
Single<WithoutArgs<CoinductiveParser>>,
|
||||
Single<WithoutArgs<ColdParser>>,
|
||||
Single<WithoutArgs<ConstContinueParser>>,
|
||||
|
|
@ -197,8 +223,10 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<MacroEscapeParser>>,
|
||||
Single<WithoutArgs<MarkerParser>>,
|
||||
Single<WithoutArgs<MayDangleParser>>,
|
||||
Single<WithoutArgs<NoCoreParser>>,
|
||||
Single<WithoutArgs<NoImplicitPreludeParser>>,
|
||||
Single<WithoutArgs<NoMangleParser>>,
|
||||
Single<WithoutArgs<NoStdParser>>,
|
||||
Single<WithoutArgs<NonExhaustiveParser>>,
|
||||
Single<WithoutArgs<ParenSugarParser>>,
|
||||
Single<WithoutArgs<PassByValueParser>>,
|
||||
|
|
@ -206,6 +234,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<ProcMacroAttributeParser>>,
|
||||
Single<WithoutArgs<ProcMacroParser>>,
|
||||
Single<WithoutArgs<PubTransparentParser>>,
|
||||
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
|
||||
Single<WithoutArgs<SpecializationTraitParser>>,
|
||||
Single<WithoutArgs<StdInternalSymbolParser>>,
|
||||
Single<WithoutArgs<TrackCallerParser>>,
|
||||
|
|
@ -235,6 +264,8 @@ pub trait Stage: Sized + 'static + Sealed {
|
|||
) -> ErrorGuaranteed;
|
||||
|
||||
fn should_emit(&self) -> ShouldEmit;
|
||||
|
||||
fn id_is_crate_root(id: Self::Id) -> bool;
|
||||
}
|
||||
|
||||
// allow because it's a sealed trait
|
||||
|
|
@ -250,16 +281,16 @@ impl Stage for Early {
|
|||
sess: &'sess Session,
|
||||
diag: impl for<'x> Diagnostic<'x>,
|
||||
) -> ErrorGuaranteed {
|
||||
if self.emit_errors.should_emit() {
|
||||
sess.dcx().emit_err(diag)
|
||||
} else {
|
||||
sess.dcx().create_err(diag).delay_as_bug()
|
||||
}
|
||||
self.should_emit().emit_err(sess.dcx().create_err(diag))
|
||||
}
|
||||
|
||||
fn should_emit(&self) -> ShouldEmit {
|
||||
self.emit_errors
|
||||
}
|
||||
|
||||
fn id_is_crate_root(id: Self::Id) -> bool {
|
||||
id == CRATE_NODE_ID
|
||||
}
|
||||
}
|
||||
|
||||
// allow because it's a sealed trait
|
||||
|
|
@ -281,6 +312,10 @@ impl Stage for Late {
|
|||
fn should_emit(&self) -> ShouldEmit {
|
||||
ShouldEmit::ErrorsAndLints
|
||||
}
|
||||
|
||||
fn id_is_crate_root(id: Self::Id) -> bool {
|
||||
id == CRATE_HIR_ID
|
||||
}
|
||||
}
|
||||
|
||||
/// used when parsing attributes for miscellaneous things *before* ast lowering
|
||||
|
|
@ -301,6 +336,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
|
|||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
|
||||
/// Whether it is an inner or outer attribute
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
|
||||
/// The expected structure of the attribute.
|
||||
///
|
||||
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
|
||||
|
|
@ -319,7 +357,10 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
|
|||
/// must be delayed until after HIR is built. This method will take care of the details of
|
||||
/// that.
|
||||
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
|
||||
if !self.stage.should_emit().should_emit() {
|
||||
if !matches!(
|
||||
self.stage.should_emit(),
|
||||
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let id = self.target_id;
|
||||
|
|
@ -382,6 +423,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
|
||||
}),
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -392,6 +434,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -402,6 +445,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedList,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -412,6 +456,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNoArgs,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -423,6 +468,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIdentifier,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +481,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNameValue(name),
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +493,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::DuplicateKey(key),
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -458,6 +506,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::UnexpectedLiteral,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -468,6 +517,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSingleArgument,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -478,13 +528,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
/// produces an error along the lines of `expected one of [foo, meow]`
|
||||
pub(crate) fn expected_specific_argument(
|
||||
&self,
|
||||
span: Span,
|
||||
possibilities: Vec<&'static str>,
|
||||
possibilities: &[Symbol],
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
|
|
@ -496,13 +548,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
/// produces an error along the lines of `expected one of [foo, meow] as an argument`.
|
||||
/// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument).
|
||||
pub(crate) fn expected_specific_argument_and_list(
|
||||
&self,
|
||||
span: Span,
|
||||
possibilities: Vec<&'static str>,
|
||||
possibilities: &[Symbol],
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
|
|
@ -514,13 +569,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: true,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
/// produces an error along the lines of `expected one of ["foo", "meow"]`
|
||||
pub(crate) fn expected_specific_argument_strings(
|
||||
&self,
|
||||
span: Span,
|
||||
possibilities: Vec<&'static str>,
|
||||
possibilities: &[Symbol],
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
|
|
@ -532,6 +589,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: true,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -567,7 +625,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
|
|||
/// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
|
||||
pub(crate) target_id: S::Id,
|
||||
|
||||
emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
|
||||
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
|
||||
}
|
||||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
|
|
@ -620,343 +678,28 @@ pub enum OmitDoc {
|
|||
Skip,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ShouldEmit {
|
||||
/// The operations will emit errors, and lints, and errors are fatal.
|
||||
///
|
||||
/// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`.
|
||||
/// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible.
|
||||
EarlyFatal { also_emit_lints: bool },
|
||||
/// The operation will emit errors and lints.
|
||||
/// This is usually what you need.
|
||||
ErrorsAndLints,
|
||||
/// The operation will emit *not* errors and lints.
|
||||
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`.
|
||||
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::ErrorsAndLints`.
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl ShouldEmit {
|
||||
pub fn should_emit(&self) -> bool {
|
||||
pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed {
|
||||
match self {
|
||||
ShouldEmit::ErrorsAndLints => true,
|
||||
ShouldEmit::Nothing => false,
|
||||
ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(),
|
||||
ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(),
|
||||
ShouldEmit::ErrorsAndLints => diag.emit(),
|
||||
ShouldEmit::Nothing => diag.delay_as_bug(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
pub(crate) tools: Vec<Symbol>,
|
||||
features: Option<&'sess Features>,
|
||||
sess: &'sess Session,
|
||||
stage: S,
|
||||
|
||||
/// *Only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Early> {
|
||||
/// This method allows you to parse attributes *before* you have access to features or tools.
|
||||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
///
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during
|
||||
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
|
||||
/// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
///
|
||||
/// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
|
||||
/// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
|
||||
/// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
) -> Option<Attribute> {
|
||||
let mut p = Self {
|
||||
features,
|
||||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
sess,
|
||||
stage: Early { emit_errors: ShouldEmit::Nothing },
|
||||
};
|
||||
let mut parsed = p.parse_attribute_list(
|
||||
attrs,
|
||||
target_span,
|
||||
target_node_id,
|
||||
OmitDoc::Skip,
|
||||
std::convert::identity,
|
||||
|_lint| {
|
||||
panic!("can't emit lints here for now (nothing uses this atm)");
|
||||
},
|
||||
);
|
||||
assert!(parsed.len() <= 1);
|
||||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn parse_single<T>(
|
||||
sess: &'sess Session,
|
||||
attr: &ast::Attribute,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
|
||||
template: &AttributeTemplate,
|
||||
) -> T {
|
||||
let mut parser = Self {
|
||||
features,
|
||||
tools: Vec::new(),
|
||||
parse_only: None,
|
||||
sess,
|
||||
stage: Early { emit_errors },
|
||||
};
|
||||
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
|
||||
panic!("parse_single called on a doc attr")
|
||||
};
|
||||
let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
|
||||
let path = meta_parser.path();
|
||||
let args = meta_parser.args();
|
||||
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: &mut parser,
|
||||
target_span,
|
||||
target_id: target_node_id,
|
||||
emit_lint: &mut |_lint| {
|
||||
panic!("can't emit lints here for now (nothing uses this atm)");
|
||||
},
|
||||
},
|
||||
attr_span: attr.span,
|
||||
template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
parse_fn(&mut cx, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub fn new(
|
||||
sess: &'sess Session,
|
||||
features: &'sess Features,
|
||||
tools: Vec<Symbol>,
|
||||
stage: S,
|
||||
) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
&self.sess
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &'sess Features {
|
||||
self.features.expect("features not available at this point in the compiler")
|
||||
}
|
||||
|
||||
pub(crate) fn features_option(&self) -> Option<&'sess Features> {
|
||||
self.features
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
self.sess().dcx()
|
||||
}
|
||||
|
||||
/// Parse a list of attributes.
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
target_span: Span,
|
||||
target_id: S::Id,
|
||||
omit_doc: OmitDoc,
|
||||
|
||||
lower_span: impl Copy + Fn(Span) -> Span,
|
||||
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
let mut attr_paths = Vec::new();
|
||||
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
if !attr.has_name(expected) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// doc still contains a non-literal. You might say, when we're lowering attributes
|
||||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these.
|
||||
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &attr.kind {
|
||||
ast::AttrKind::DocComment(comment_kind, symbol) => {
|
||||
if omit_doc == OmitDoc::Skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: *comment_kind,
|
||||
span: lower_span(attr.span),
|
||||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
attr_paths.push(PathParser::Ast(&n.item.path));
|
||||
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let path = parser.path();
|
||||
let args = parser.args();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
|
||||
for accept in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
template: &accept.template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
|
||||
(accept.accept_fn)(&mut cx, args)
|
||||
}
|
||||
} else {
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
// forgot to parse their fancy new attribute. Let's warn them in any case.
|
||||
// If you are that person, and you really think your attribute should
|
||||
// remain unparsed, carefully read the documentation in this module and if
|
||||
// you still think so you can add an exception to this assertion.
|
||||
|
||||
// FIXME(jdonszelmann): convert other attributes, and check with this that
|
||||
// we caught em all
|
||||
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
|
||||
// assert!(
|
||||
// self.tools.contains(&parts[0]) || true,
|
||||
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
|
||||
// "attribute {path} wasn't parsed and isn't a know tool attribute",
|
||||
// );
|
||||
|
||||
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
|
||||
path: AttrPath::from_ast(&n.item.path),
|
||||
args: self.lower_attr_args(&n.item.args, lower_span),
|
||||
id: HashIgnoredAttrId { attr_id: attr.id },
|
||||
style: attr.style,
|
||||
span: lower_span(attr.span),
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &S::parsers().finalizers {
|
||||
if let Some(attr) = f(&mut FinalizeContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
all_attrs: &attr_paths,
|
||||
}) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
}
|
||||
|
||||
attributes.extend(parsed_attributes);
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
/// Returns whether there is a parser for an attribute with this name
|
||||
pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
|
||||
Late::parsers().accepters.contains_key(path)
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
|
||||
match args {
|
||||
ast::AttrArgs::Empty => AttrArgs::Empty,
|
||||
ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
ast::AttrArgs::Eq { eq_span, expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) =
|
||||
ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().span_delayed_bug(
|
||||
args.span().unwrap_or(DUMMY_SP),
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
ast::MetaItemLit {
|
||||
symbol: sym::dummy,
|
||||
suffix: None,
|
||||
kind: ast::LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a single integer.
|
||||
///
|
||||
/// Used by attributes that take a single integer as argument, such as
|
||||
/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
|
||||
/// `cx` is the context given to the attribute.
|
||||
/// `args` is the parser for the attribute arguments.
|
||||
pub(crate) fn parse_single_integer<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<u128> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
let Some(single) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
let Some(lit) = single.lit() else {
|
||||
cx.expected_integer_literal(single.span());
|
||||
return None;
|
||||
};
|
||||
let LitKind::Int(num, _ty) = lit.kind else {
|
||||
cx.expected_integer_literal(single.span());
|
||||
return None;
|
||||
};
|
||||
Some(num.0)
|
||||
}
|
||||
|
|
|
|||
362
compiler/rustc_attr_parsing/src/interface.rs
Normal file
362
compiler/rustc_attr_parsing/src/interface.rs
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::lints::AttributeLint;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemParser, PathParser};
|
||||
use crate::{Early, Late, OmitDoc, ShouldEmit};
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
pub(crate) tools: Vec<Symbol>,
|
||||
pub(crate) features: Option<&'sess Features>,
|
||||
pub(crate) sess: &'sess Session,
|
||||
pub(crate) stage: S,
|
||||
|
||||
/// *Only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Early> {
|
||||
/// This method allows you to parse attributes *before* you have access to features or tools.
|
||||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
///
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during
|
||||
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
|
||||
/// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
///
|
||||
/// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
|
||||
/// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
|
||||
/// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
) -> Option<Attribute> {
|
||||
Self::parse_limited_should_emit(
|
||||
sess,
|
||||
attrs,
|
||||
sym,
|
||||
target_span,
|
||||
target_node_id,
|
||||
features,
|
||||
ShouldEmit::Nothing,
|
||||
)
|
||||
}
|
||||
|
||||
/// Usually you want `parse_limited`, which defaults to no errors.
|
||||
pub fn parse_limited_should_emit(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<Attribute> {
|
||||
let mut parsed = Self::parse_limited_all(
|
||||
sess,
|
||||
attrs,
|
||||
Some(sym),
|
||||
Target::Crate, // Does not matter, we're not going to emit errors anyways
|
||||
target_span,
|
||||
target_node_id,
|
||||
features,
|
||||
should_emit,
|
||||
);
|
||||
assert!(parsed.len() <= 1);
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn parse_limited_all(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
parse_only: Option<Symbol>,
|
||||
target: Target,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
) -> Vec<Attribute> {
|
||||
let mut p =
|
||||
Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
|
||||
p.parse_attribute_list(
|
||||
attrs,
|
||||
target_span,
|
||||
target_node_id,
|
||||
target,
|
||||
OmitDoc::Skip,
|
||||
std::convert::identity,
|
||||
|lint| {
|
||||
crate::lints::emit_attribute_lint(&lint, sess);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_single<T>(
|
||||
sess: &'sess Session,
|
||||
attr: &ast::Attribute,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
|
||||
template: &AttributeTemplate,
|
||||
) -> Option<T> {
|
||||
let mut parser = Self {
|
||||
features,
|
||||
tools: Vec::new(),
|
||||
parse_only: None,
|
||||
sess,
|
||||
stage: Early { emit_errors },
|
||||
};
|
||||
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
|
||||
panic!("parse_single called on a doc attr")
|
||||
};
|
||||
let parts =
|
||||
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
|
||||
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
|
||||
let path = meta_parser.path();
|
||||
let args = meta_parser.args();
|
||||
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: &mut parser,
|
||||
target_span,
|
||||
target_id: target_node_id,
|
||||
emit_lint: &mut |lint| {
|
||||
crate::lints::emit_attribute_lint(&lint, sess);
|
||||
},
|
||||
},
|
||||
attr_span: attr.span,
|
||||
attr_style: attr.style,
|
||||
template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
parse_fn(&mut cx, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub fn new(
|
||||
sess: &'sess Session,
|
||||
features: &'sess Features,
|
||||
tools: Vec<Symbol>,
|
||||
stage: S,
|
||||
) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
&self.sess
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &'sess Features {
|
||||
self.features.expect("features not available at this point in the compiler")
|
||||
}
|
||||
|
||||
pub(crate) fn features_option(&self) -> Option<&'sess Features> {
|
||||
self.features
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
self.sess().dcx()
|
||||
}
|
||||
|
||||
/// Parse a list of attributes.
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
target_span: Span,
|
||||
target_id: S::Id,
|
||||
target: Target,
|
||||
omit_doc: OmitDoc,
|
||||
|
||||
lower_span: impl Copy + Fn(Span) -> Span,
|
||||
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
let mut attr_paths = Vec::new();
|
||||
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
if !attr.has_name(expected) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// doc still contains a non-literal. You might say, when we're lowering attributes
|
||||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these.
|
||||
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &attr.kind {
|
||||
ast::AttrKind::DocComment(comment_kind, symbol) => {
|
||||
if omit_doc == OmitDoc::Skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: *comment_kind,
|
||||
span: lower_span(attr.span),
|
||||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
|
||||
|
||||
let parts =
|
||||
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
|
||||
let Some(parser) = MetaItemParser::from_attr(
|
||||
n,
|
||||
&parts,
|
||||
&self.sess.psess,
|
||||
self.stage.should_emit(),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
let path = parser.path();
|
||||
let args = parser.args();
|
||||
for accept in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
attr_style: attr.style,
|
||||
template: &accept.template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
|
||||
(accept.accept_fn)(&mut cx, args);
|
||||
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
Self::check_target(&accept.allowed_targets, target, &mut cx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
// forgot to parse their fancy new attribute. Let's warn them in any case.
|
||||
// If you are that person, and you really think your attribute should
|
||||
// remain unparsed, carefully read the documentation in this module and if
|
||||
// you still think so you can add an exception to this assertion.
|
||||
|
||||
// FIXME(jdonszelmann): convert other attributes, and check with this that
|
||||
// we caught em all
|
||||
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
|
||||
// assert!(
|
||||
// self.tools.contains(&parts[0]) || true,
|
||||
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
|
||||
// "attribute {path} wasn't parsed and isn't a know tool attribute",
|
||||
// );
|
||||
|
||||
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
|
||||
path: AttrPath::from_ast(&n.item.path),
|
||||
args: self.lower_attr_args(&n.item.args, lower_span),
|
||||
id: HashIgnoredAttrId { attr_id: attr.id },
|
||||
style: attr.style,
|
||||
span: lower_span(attr.span),
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &S::parsers().finalizers {
|
||||
if let Some(attr) = f(&mut FinalizeContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
all_attrs: &attr_paths,
|
||||
}) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
}
|
||||
|
||||
attributes.extend(parsed_attributes);
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
/// Returns whether there is a parser for an attribute with this name
|
||||
pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
|
||||
Late::parsers().accepters.contains_key(path)
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
|
||||
match args {
|
||||
ast::AttrArgs::Empty => AttrArgs::Empty,
|
||||
ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
ast::AttrArgs::Eq { eq_span, expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) =
|
||||
ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().span_delayed_bug(
|
||||
args.span().unwrap_or(DUMMY_SP),
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
ast::MetaItemLit {
|
||||
symbol: sym::dummy,
|
||||
suffix: None,
|
||||
kind: ast::LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,23 +79,37 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
/// All the individual attribute parsers for each of rustc's built-in attributes.
|
||||
mod attributes;
|
||||
|
||||
/// All the important types given to attribute parsers when parsing
|
||||
pub(crate) mod context;
|
||||
mod lints;
|
||||
|
||||
/// Code that other crates interact with, to actually parse a list (or sometimes single)
|
||||
/// attribute.
|
||||
mod interface;
|
||||
|
||||
/// Despite this entire module called attribute parsing and the term being a little overloaded,
|
||||
/// in this module the code lives that actually breaks up tokenstreams into semantic pieces of attributes,
|
||||
/// like lists or name-value pairs.
|
||||
pub mod parser;
|
||||
|
||||
mod lints;
|
||||
mod session_diagnostics;
|
||||
mod target_checking;
|
||||
pub mod validate_attr;
|
||||
|
||||
pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
|
||||
pub use attributes::cfg_old::*;
|
||||
pub use attributes::util::{
|
||||
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
|
||||
};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
|
||||
pub use context::{Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use interface::AttributeParser;
|
||||
pub use lints::emit_attribute_lint;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use rustc_errors::{DiagArgValue, LintEmitter};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
|
||||
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emitter: L) {
|
||||
let AttributeLint { id, span, kind } = lint;
|
||||
|
||||
match kind {
|
||||
|
|
@ -28,11 +31,66 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
|
|||
},
|
||||
);
|
||||
}
|
||||
AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter
|
||||
.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
*id,
|
||||
*span,
|
||||
session_diagnostics::IllFormedAttributeInput {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
),
|
||||
},
|
||||
),
|
||||
AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
|
||||
*id,
|
||||
*first_span,
|
||||
session_diagnostics::EmptyAttributeList { attr_span: *first_span },
|
||||
),
|
||||
AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter
|
||||
.emit_node_span_lint(
|
||||
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
|
||||
if name.segments[0].name == sym::deprecated
|
||||
&& ![
|
||||
Target::Closure,
|
||||
Target::Expression,
|
||||
Target::Statement,
|
||||
Target::Arm,
|
||||
Target::MacroCall,
|
||||
]
|
||||
.contains(target)
|
||||
{
|
||||
rustc_session::lint::builtin::USELESS_DEPRECATED
|
||||
} else {
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
|
||||
},
|
||||
*id,
|
||||
*span,
|
||||
session_diagnostics::InvalidTargetLint {
|
||||
name: name.clone(),
|
||||
target: target.plural_name(),
|
||||
applied: DiagArgValue::StrListSepByAnd(
|
||||
applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
|
||||
),
|
||||
only,
|
||||
attr_span: *span,
|
||||
},
|
||||
),
|
||||
|
||||
&AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
|
||||
lint_emitter.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
|
||||
*id,
|
||||
*span,
|
||||
session_diagnostics::InvalidAttrStyle {
|
||||
name: name.clone(),
|
||||
is_used_as_inner,
|
||||
target_span: (!is_used_as_inner).then_some(target_span),
|
||||
target,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,45 +3,30 @@
|
|||
//!
|
||||
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use rustc_ast::token::{self, Delimiter, Token};
|
||||
use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
|
||||
use rustc_ast::token::{self, Delimiter, MetaVarKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_errors::{Diag, PResult};
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::{Parser, PathStyle, token_descr};
|
||||
use rustc_session::errors::{create_lit_error, report_lit_error};
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
pub struct SegmentIterator<'a> {
|
||||
offset: usize,
|
||||
path: &'a PathParser<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SegmentIterator<'a> {
|
||||
type Item = &'a Ident;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.path.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let res = match self.path {
|
||||
PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
|
||||
PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
|
||||
};
|
||||
|
||||
self.offset += 1;
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
use crate::ShouldEmit;
|
||||
use crate::session_diagnostics::{
|
||||
InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
|
||||
MetaBadDelimSugg, SuffixedLiteralInAttribute,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PathParser<'a> {
|
||||
Ast(&'a Path),
|
||||
Attr(AttrPath),
|
||||
}
|
||||
pub struct PathParser<'a>(pub Cow<'a, Path>);
|
||||
|
||||
impl<'a> PathParser<'a> {
|
||||
pub fn get_attribute_path(&self) -> hir::AttrPath {
|
||||
|
|
@ -52,21 +37,15 @@ impl<'a> PathParser<'a> {
|
|||
}
|
||||
|
||||
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
|
||||
SegmentIterator { offset: 0, path: self }
|
||||
self.0.segments.iter().map(|seg| &seg.ident)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.span,
|
||||
PathParser::Attr(attr_path) => attr_path.span,
|
||||
}
|
||||
self.0.span
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.segments.len(),
|
||||
PathParser::Attr(attr_path) => attr_path.segments.len(),
|
||||
}
|
||||
self.0.segments.len()
|
||||
}
|
||||
|
||||
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
|
||||
|
|
@ -99,10 +78,7 @@ impl<'a> PathParser<'a> {
|
|||
|
||||
impl Display for PathParser<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
|
||||
PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
|
||||
}
|
||||
write!(f, "{}", pprust::path_to_string(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,21 +99,39 @@ impl<'a> ArgParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
match value {
|
||||
pub fn from_attr_args<'sess>(
|
||||
value: &'a AttrArgs,
|
||||
parts: &[Symbol],
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<Self> {
|
||||
Some(match value {
|
||||
AttrArgs::Empty => Self::NoArgs,
|
||||
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
|
||||
Self::List(MetaItemListParser::new(args, dcx))
|
||||
}
|
||||
AttrArgs::Delimited(args) => {
|
||||
Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
|
||||
// The arguments of rustc_dummy are not validated if the arguments are delimited
|
||||
if parts == &[sym::rustc_dummy] {
|
||||
return Some(ArgParser::List(MetaItemListParser {
|
||||
sub_parsers: ThinVec::new(),
|
||||
span: args.dspan.entire(),
|
||||
}));
|
||||
}
|
||||
|
||||
if args.delim != Delimiter::Parenthesis {
|
||||
psess.dcx().emit_err(MetaBadDelim {
|
||||
span: args.dspan.entire(),
|
||||
sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
|
||||
});
|
||||
return None;
|
||||
}
|
||||
|
||||
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
|
||||
}
|
||||
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
|
||||
eq_span: *eq_span,
|
||||
value: expr_to_lit(dcx, &expr, *eq_span),
|
||||
value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
|
||||
value_span: expr.span,
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is a list
|
||||
|
|
@ -249,11 +243,16 @@ impl<'a> Debug for MetaItemParser<'a> {
|
|||
impl<'a> MetaItemParser<'a> {
|
||||
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
||||
/// [`ast::Attribute`](rustc_ast::Attribute)
|
||||
pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
Self {
|
||||
path: PathParser::Ast(&attr.item.path),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, dcx),
|
||||
}
|
||||
pub fn from_attr<'sess>(
|
||||
attr: &'a NormalAttr,
|
||||
parts: &[Symbol],
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<Self> {
|
||||
Some(Self {
|
||||
path: PathParser(Cow::Borrowed(&attr.item.path)),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,215 +317,248 @@ impl NameValueParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
lit
|
||||
fn expr_to_lit(
|
||||
psess: &ParseSess,
|
||||
expr: &Expr,
|
||||
span: Span,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<MetaItemLit> {
|
||||
if let ExprKind::Lit(token_lit) = expr.kind {
|
||||
let res = MetaItemLit::from_token_lit(token_lit, expr.span);
|
||||
match res {
|
||||
Ok(lit) => {
|
||||
if token_lit.suffix.is_some() {
|
||||
should_emit.emit_err(
|
||||
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
None
|
||||
} else {
|
||||
if !lit.kind.is_unsuffixed() {
|
||||
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
||||
should_emit.emit_err(
|
||||
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
}
|
||||
|
||||
Some(lit)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let guar = report_lit_error(psess, err, token_lit, expr.span);
|
||||
let lit = MetaItemLit {
|
||||
symbol: token_lit.symbol,
|
||||
suffix: token_lit.suffix,
|
||||
kind: LitKind::Err(guar),
|
||||
span: expr.span,
|
||||
};
|
||||
Some(lit)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let guar = dcx.span_delayed_bug(
|
||||
span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
|
||||
if matches!(should_emit, ShouldEmit::Nothing) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Example cases:
|
||||
// - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
|
||||
// - `#[foo = include_str!("nonexistent-file.rs")]`:
|
||||
// results in `ast::ExprKind::Err`. In that case we delay
|
||||
// the error because an earlier error will have already
|
||||
// been reported.
|
||||
let msg = "attribute value must be a literal";
|
||||
let err = psess.dcx().struct_span_err(span, msg);
|
||||
should_emit.emit_err(err);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct MetaItemListParserContext<'a, 'sess> {
|
||||
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
|
||||
inside_delimiters: Peekable<TokenStreamIter<'a>>,
|
||||
dcx: DiagCtxtHandle<'sess>,
|
||||
parser: &'a mut Parser<'sess>,
|
||||
should_emit: ShouldEmit,
|
||||
}
|
||||
|
||||
impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
||||
fn done(&mut self) -> bool {
|
||||
self.inside_delimiters.peek().is_none()
|
||||
fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
|
||||
let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
|
||||
self.unsuffixed_meta_item_from_lit(token_lit)
|
||||
}
|
||||
|
||||
fn next_path(&mut self) -> Option<AttrPath> {
|
||||
// FIXME: Share code with `parse_path`.
|
||||
let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
|
||||
|
||||
match tt.as_deref()? {
|
||||
&TokenTree::Token(
|
||||
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
|
||||
_,
|
||||
) => {
|
||||
// here we have either an ident or pathsep `::`.
|
||||
|
||||
let mut segments = if let &token::Ident(name, _) = kind {
|
||||
// when we lookahead another pathsep, more path's coming
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
vec![Ident::new(name, span)]
|
||||
} else {
|
||||
// else we have a single identifier path, that's all
|
||||
return Some(AttrPath {
|
||||
segments: vec![Ident::new(name, span)].into_boxed_slice(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// if `::` is all we get, we just got a path root
|
||||
vec![Ident::new(kw::PathRoot, span)]
|
||||
};
|
||||
|
||||
// one segment accepted. accept n more
|
||||
loop {
|
||||
// another ident?
|
||||
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
|
||||
self.inside_delimiters
|
||||
.next()
|
||||
.map(|tt| TokenTree::uninterpolate(tt))
|
||||
.as_deref()
|
||||
{
|
||||
segments.push(Ident::new(name, span));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
// stop unless we see another `::`
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let span = span.with_hi(segments.last().unwrap().span.hi());
|
||||
Some(AttrPath { segments: segments.into_boxed_slice(), span })
|
||||
fn unsuffixed_meta_item_from_lit(
|
||||
&mut self,
|
||||
token_lit: token::Lit,
|
||||
) -> PResult<'sess, MetaItemLit> {
|
||||
let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
|
||||
Ok(lit) => lit,
|
||||
Err(err) => {
|
||||
return Err(create_lit_error(
|
||||
&self.parser.psess,
|
||||
err,
|
||||
token_lit,
|
||||
self.parser.prev_token_uninterpolated_span(),
|
||||
));
|
||||
}
|
||||
TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
|
||||
_ => {
|
||||
// malformed attributes can get here. We can't crash, but somewhere else should've
|
||||
// already warned for this.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn value(&mut self) -> Option<MetaItemLit> {
|
||||
match self.inside_delimiters.next() {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
|
||||
MetaItemListParserContext {
|
||||
inside_delimiters: inner_tokens.iter().peekable(),
|
||||
dcx: self.dcx,
|
||||
}
|
||||
.value()
|
||||
}
|
||||
Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
|
||||
///
|
||||
/// parses a path followed be either:
|
||||
/// 1. nothing (a word attr)
|
||||
/// 2. a parenthesized list
|
||||
/// 3. an equals sign and a literal (name-value)
|
||||
///
|
||||
/// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
|
||||
/// where no path is given before the literal
|
||||
///
|
||||
/// Some exceptions too for interpolated attributes which are already pre-processed
|
||||
fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
|
||||
// a list element is either a literal
|
||||
if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
|
||||
&& let Some(lit) = MetaItemLit::from_token(token)
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
return Some(MetaItemOrLitParser::Lit(lit));
|
||||
} else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
return MetaItemListParserContext {
|
||||
inside_delimiters: inner_tokens.iter().peekable(),
|
||||
dcx: self.dcx,
|
||||
}
|
||||
.next();
|
||||
if !lit.kind.is_unsuffixed() {
|
||||
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
||||
self.should_emit.emit_err(
|
||||
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
}
|
||||
|
||||
// or a path.
|
||||
let path = self.next_path()?;
|
||||
|
||||
// Paths can be followed by:
|
||||
// - `(more meta items)` (another list)
|
||||
// - `= lit` (a name-value)
|
||||
// - nothing
|
||||
Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
|
||||
Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::List(MetaItemListParser::new_tts(
|
||||
inner_tokens.iter(),
|
||||
dspan.entire(),
|
||||
self.dcx,
|
||||
)),
|
||||
}
|
||||
}
|
||||
Some(TokenTree::Delimited(_, ..)) => {
|
||||
self.inside_delimiters.next();
|
||||
// self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
|
||||
return None;
|
||||
}
|
||||
Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
let value = self.value()?;
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::NameValue(NameValueParser {
|
||||
eq_span: *span,
|
||||
value_span: value.span,
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
|
||||
}))
|
||||
Ok(lit)
|
||||
}
|
||||
|
||||
fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
|
||||
let mut sub_parsers = Vec::new();
|
||||
|
||||
while !self.done() {
|
||||
let Some(n) = self.next() else {
|
||||
continue;
|
||||
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
|
||||
if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
|
||||
return if has_meta_form {
|
||||
let attr_item = self
|
||||
.parser
|
||||
.eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
|
||||
MetaItemListParserContext { parser: this, should_emit: self.should_emit }
|
||||
.parse_attr_item()
|
||||
})
|
||||
.unwrap();
|
||||
Ok(attr_item)
|
||||
} else {
|
||||
self.parser.unexpected_any()
|
||||
};
|
||||
sub_parsers.push(n);
|
||||
}
|
||||
|
||||
match self.inside_delimiters.peek() {
|
||||
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
let path = self.parser.parse_path(PathStyle::Mod)?;
|
||||
|
||||
// Check style of arguments that this meta item has
|
||||
let args = if self.parser.check(exp!(OpenParen)) {
|
||||
let start = self.parser.token.span;
|
||||
let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
|
||||
MetaItemListParserContext { parser, should_emit: self.should_emit }
|
||||
.parse_meta_item_inner()
|
||||
})?;
|
||||
let end = self.parser.prev_token.span;
|
||||
ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
|
||||
} else if self.parser.eat(exp!(Eq)) {
|
||||
let eq_span = self.parser.prev_token.span;
|
||||
let value = self.parse_unsuffixed_meta_item_lit()?;
|
||||
|
||||
ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
|
||||
} else {
|
||||
ArgParser::NoArgs
|
||||
};
|
||||
|
||||
Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
|
||||
}
|
||||
|
||||
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
|
||||
if let Some(token_lit) = self.parser.eat_token_lit() {
|
||||
// If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
|
||||
Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
|
||||
} else {
|
||||
let prev_pros = self.parser.approx_token_stream_pos();
|
||||
match self.parse_attr_item() {
|
||||
Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
|
||||
Err(err) => {
|
||||
// If `parse_attr_item` made any progress, it likely has a more precise error we should prefer
|
||||
// If it didn't make progress we use the `expected_lit` from below
|
||||
if self.parser.approx_token_stream_pos() != prev_pros {
|
||||
Err(err)
|
||||
} else {
|
||||
err.cancel();
|
||||
Err(self.expected_lit())
|
||||
}
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_lit(&mut self) -> Diag<'sess> {
|
||||
let mut err = InvalidMetaItem {
|
||||
span: self.parser.token.span,
|
||||
descr: token_descr(&self.parser.token),
|
||||
quote_ident_sugg: None,
|
||||
remove_neg_sugg: None,
|
||||
};
|
||||
|
||||
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
|
||||
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
|
||||
// when macro metavariables are involved.
|
||||
if self.parser.prev_token == token::Eq
|
||||
&& let token::Ident(..) = self.parser.token.kind
|
||||
{
|
||||
let before = self.parser.token.span.shrink_to_lo();
|
||||
while let token::Ident(..) = self.parser.token.kind {
|
||||
self.parser.bump();
|
||||
}
|
||||
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
|
||||
before,
|
||||
after: self.parser.prev_token.span.shrink_to_hi(),
|
||||
});
|
||||
}
|
||||
|
||||
if self.parser.token == token::Minus
|
||||
&& self
|
||||
.parser
|
||||
.look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
|
||||
{
|
||||
err.remove_neg_sugg =
|
||||
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
|
||||
self.parser.bump();
|
||||
self.parser.bump();
|
||||
}
|
||||
|
||||
self.parser.dcx().create_err(err)
|
||||
}
|
||||
|
||||
fn parse(
|
||||
tokens: TokenStream,
|
||||
psess: &'sess ParseSess,
|
||||
span: Span,
|
||||
should_emit: ShouldEmit,
|
||||
) -> PResult<'sess, MetaItemListParser<'static>> {
|
||||
let mut parser = Parser::new(psess, tokens, None);
|
||||
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
|
||||
|
||||
// Presumably, the majority of the time there will only be one attr.
|
||||
let mut sub_parsers = ThinVec::with_capacity(1);
|
||||
while this.parser.token != token::Eof {
|
||||
sub_parsers.push(this.parse_meta_item_inner()?);
|
||||
|
||||
if !this.parser.eat(exp!(Comma)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MetaItemListParser { sub_parsers, span }
|
||||
if parser.token != token::Eof {
|
||||
parser.unexpected()?;
|
||||
}
|
||||
|
||||
Ok(MetaItemListParser { sub_parsers, span })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetaItemListParser<'a> {
|
||||
sub_parsers: Vec<MetaItemOrLitParser<'a>>,
|
||||
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParser<'a> {
|
||||
fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
|
||||
}
|
||||
|
||||
fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
|
||||
fn new<'sess>(
|
||||
delim: &'a DelimArgs,
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<Self> {
|
||||
match MetaItemListParserContext::parse(
|
||||
delim.tokens.clone(),
|
||||
psess,
|
||||
delim.dspan.entire(),
|
||||
should_emit,
|
||||
) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
should_emit.emit_err(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use std::num::IntErrorKind;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::{self as ast, AttrStyle, Path};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
|
||||
};
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_hir::AttrPath;
|
||||
use rustc_hir::{AttrPath, Target};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
|
@ -459,6 +459,34 @@ pub(crate) struct NullOnLinkSection {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_null_on_objc_class)]
|
||||
pub(crate) struct NullOnObjcClass {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_null_on_objc_selector)]
|
||||
pub(crate) struct NullOnObjcSelector {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_objc_class_expected_string_literal)]
|
||||
pub(crate) struct ObjcClassExpectedStringLiteral {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_objc_selector_expected_string_literal)]
|
||||
pub(crate) struct ObjcSelectorExpectedStringLiteral {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
|
|
@ -480,6 +508,32 @@ pub(crate) struct EmptyAttributeList {
|
|||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_invalid_target_lint)]
|
||||
#[warning]
|
||||
#[help]
|
||||
pub(crate) struct InvalidTargetLint {
|
||||
pub name: AttrPath,
|
||||
pub target: &'static str,
|
||||
pub applied: DiagArgValue,
|
||||
pub only: &'static str,
|
||||
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[help]
|
||||
#[diag(attr_parsing_invalid_target)]
|
||||
pub(crate) struct InvalidTarget {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
|
||||
pub span: Span,
|
||||
pub name: AttrPath,
|
||||
pub target: &'static str,
|
||||
pub applied: DiagArgValue,
|
||||
pub only: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
|
||||
pub(crate) struct InvalidAlignmentValue {
|
||||
|
|
@ -498,6 +552,7 @@ pub(crate) struct ReprIdent {
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
|
||||
#[help]
|
||||
#[note]
|
||||
pub(crate) struct UnrecognizedReprHint {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
|
@ -531,7 +586,7 @@ pub(crate) struct LinkOrdinalOutOfRange {
|
|||
pub ordinal: u128,
|
||||
}
|
||||
|
||||
pub(crate) enum AttributeParseErrorReason {
|
||||
pub(crate) enum AttributeParseErrorReason<'a> {
|
||||
ExpectedNoArgs,
|
||||
ExpectedStringLiteral {
|
||||
byte_string: Option<Span>,
|
||||
|
|
@ -544,7 +599,7 @@ pub(crate) enum AttributeParseErrorReason {
|
|||
ExpectedNameValue(Option<Symbol>),
|
||||
DuplicateKey(Symbol),
|
||||
ExpectedSpecificArgument {
|
||||
possibilities: Vec<&'static str>,
|
||||
possibilities: &'a [Symbol],
|
||||
strings: bool,
|
||||
/// Should we tell the user to write a list when they didn't?
|
||||
list: bool,
|
||||
|
|
@ -552,15 +607,16 @@ pub(crate) enum AttributeParseErrorReason {
|
|||
ExpectedIdentifier,
|
||||
}
|
||||
|
||||
pub(crate) struct AttributeParseError {
|
||||
pub(crate) struct AttributeParseError<'a> {
|
||||
pub(crate) span: Span,
|
||||
pub(crate) attr_span: Span,
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
pub(crate) template: AttributeTemplate,
|
||||
pub(crate) attribute: AttrPath,
|
||||
pub(crate) reason: AttributeParseErrorReason,
|
||||
pub(crate) reason: AttributeParseErrorReason<'a>,
|
||||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
||||
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
|
||||
let name = self.attribute.to_string();
|
||||
|
||||
|
|
@ -629,7 +685,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
list: false,
|
||||
} => {
|
||||
let quote = if strings { '"' } else { '`' };
|
||||
match possibilities.as_slice() {
|
||||
match possibilities {
|
||||
&[] => {}
|
||||
&[x] => {
|
||||
diag.span_label(
|
||||
|
|
@ -659,7 +715,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
list: true,
|
||||
} => {
|
||||
let quote = if strings { '"' } else { '`' };
|
||||
match possibilities.as_slice() {
|
||||
match possibilities {
|
||||
&[] => {}
|
||||
&[x] => {
|
||||
diag.span_label(
|
||||
|
|
@ -690,7 +746,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
}
|
||||
}
|
||||
|
||||
let suggestions = self.template.suggestions(false, &name);
|
||||
if let Some(link) = self.template.docs {
|
||||
diag.note(format!("for more information, visit <{link}>"));
|
||||
}
|
||||
let suggestions = self.template.suggestions(self.attr_style, &name);
|
||||
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if suggestions.len() == 1 {
|
||||
|
|
@ -705,3 +765,206 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_attr_unsafe)]
|
||||
#[note]
|
||||
pub(crate) struct InvalidAttrUnsafe {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub name: Path,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
|
||||
pub(crate) struct UnsafeAttrOutsideUnsafe {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
attr_parsing_unsafe_attr_outside_unsafe_suggestion,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
|
||||
#[suggestion_part(code = "unsafe(")]
|
||||
pub left: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
pub right: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_meta_bad_delim)]
|
||||
pub(crate) struct MetaBadDelim {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: MetaBadDelimSugg,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
attr_parsing_meta_bad_delim_suggestion,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub(crate) struct MetaBadDelimSugg {
|
||||
#[suggestion_part(code = "(")]
|
||||
pub open: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
pub close: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_meta_item)]
|
||||
pub(crate) struct InvalidMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: String,
|
||||
#[subdiagnostic]
|
||||
pub quote_ident_sugg: Option<InvalidMetaItemQuoteIdentSugg>,
|
||||
#[subdiagnostic]
|
||||
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")]
|
||||
pub(crate) struct InvalidMetaItemQuoteIdentSugg {
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub before: Span,
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub after: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")]
|
||||
pub(crate) struct InvalidMetaItemRemoveNegSugg {
|
||||
#[suggestion_part(code = "")]
|
||||
pub negative_sign: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_suffixed_literal_in_attribute)]
|
||||
#[help]
|
||||
pub(crate) struct SuffixedLiteralInAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_invalid_style)]
|
||||
pub(crate) struct InvalidAttrStyle {
|
||||
pub name: AttrPath,
|
||||
pub is_used_as_inner: bool,
|
||||
#[note]
|
||||
pub target_span: Option<Span>,
|
||||
pub target: Target,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_empty_link_name, code = E0454)]
|
||||
pub(crate) struct EmptyLinkName {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_link_framework_apple, code = E0455)]
|
||||
pub(crate) struct LinkFrameworkApple {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_incompatible_wasm_link)]
|
||||
pub(crate) struct IncompatibleWasmLink {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_link_requires_name, code = E0459)]
|
||||
pub(crate) struct LinkRequiresName {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_raw_dylib_no_nul)]
|
||||
pub(crate) struct RawDylibNoNul {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)]
|
||||
pub(crate) struct RawDylibOnlyWindows {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_link_modifier)]
|
||||
pub(crate) struct InvalidLinkModifier {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_multiple_modifiers)]
|
||||
pub(crate) struct MultipleModifiers {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub modifier: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_import_name_type_x86)]
|
||||
pub(crate) struct ImportNameTypeX86 {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_bundle_needs_static)]
|
||||
pub(crate) struct BundleNeedsStatic {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_whole_archive_needs_static)]
|
||||
pub(crate) struct WholeArchiveNeedsStatic {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_as_needed_compatibility)]
|
||||
pub(crate) struct AsNeededCompatibility {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_import_name_type_raw)]
|
||||
pub(crate) struct ImportNameTypeRaw {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_limit_invalid)]
|
||||
pub(crate) struct LimitInvalid<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub value_span: Span,
|
||||
pub error_str: &'a str,
|
||||
}
|
||||
|
|
|
|||
278
compiler/rustc_attr_parsing/src/target_checking.rs
Normal file
278
compiler/rustc_attr_parsing/src/target_checking.rs
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use rustc_ast::AttrStyle;
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_hir::{MethodKind, Target};
|
||||
|
||||
use crate::AttributeParser;
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::session_diagnostics::InvalidTarget;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum AllowedTargets {
|
||||
AllowList(&'static [Policy]),
|
||||
AllowListWarnRest(&'static [Policy]),
|
||||
/// Special, and not the same as `AllowList(&[Allow(Target::Crate)])`.
|
||||
/// For crate-level attributes we emit a specific set of lints to warn
|
||||
/// people about accidentally not using them on the crate.
|
||||
/// Only use this for attributes that are *exclusively* valid at the crate level.
|
||||
CrateLevel,
|
||||
}
|
||||
|
||||
pub(crate) enum AllowedResult {
|
||||
Allowed,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl AllowedTargets {
|
||||
pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
|
||||
match self {
|
||||
AllowedTargets::AllowList(list) => {
|
||||
if list.contains(&Policy::Allow(target)) {
|
||||
AllowedResult::Allowed
|
||||
} else if list.contains(&Policy::Warn(target)) {
|
||||
AllowedResult::Warn
|
||||
} else {
|
||||
AllowedResult::Error
|
||||
}
|
||||
}
|
||||
AllowedTargets::AllowListWarnRest(list) => {
|
||||
if list.contains(&Policy::Allow(target)) {
|
||||
AllowedResult::Allowed
|
||||
} else if list.contains(&Policy::Error(target)) {
|
||||
AllowedResult::Error
|
||||
} else {
|
||||
AllowedResult::Warn
|
||||
}
|
||||
}
|
||||
AllowedTargets::CrateLevel => AllowedResult::Allowed,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn allowed_targets(&self) -> Vec<Target> {
|
||||
match self {
|
||||
AllowedTargets::AllowList(list) => list,
|
||||
AllowedTargets::AllowListWarnRest(list) => list,
|
||||
AllowedTargets::CrateLevel => ALL_TARGETS,
|
||||
}
|
||||
.iter()
|
||||
.filter_map(|target| match target {
|
||||
Policy::Allow(target) => Some(*target),
|
||||
Policy::Warn(_) => None,
|
||||
Policy::Error(_) => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Policy {
|
||||
Allow(Target),
|
||||
Warn(Target),
|
||||
Error(Target),
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub(crate) fn check_target(
|
||||
allowed_targets: &AllowedTargets,
|
||||
target: Target,
|
||||
cx: &mut AcceptContext<'_, 'sess, S>,
|
||||
) {
|
||||
Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx);
|
||||
|
||||
match allowed_targets.is_allowed(target) {
|
||||
AllowedResult::Allowed => {}
|
||||
AllowedResult::Warn => {
|
||||
let allowed_targets = allowed_targets.allowed_targets();
|
||||
let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
|
||||
let name = cx.attr_path.clone();
|
||||
let attr_span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::InvalidTarget {
|
||||
name,
|
||||
target,
|
||||
only: if only { "only " } else { "" },
|
||||
applied,
|
||||
},
|
||||
attr_span,
|
||||
);
|
||||
}
|
||||
AllowedResult::Error => {
|
||||
let allowed_targets = allowed_targets.allowed_targets();
|
||||
let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
|
||||
let name = cx.attr_path.clone();
|
||||
cx.dcx().emit_err(InvalidTarget {
|
||||
span: cx.attr_span.clone(),
|
||||
name,
|
||||
target: target.plural_name(),
|
||||
only: if only { "only " } else { "" },
|
||||
applied: DiagArgValue::StrListSepByAnd(
|
||||
applied.into_iter().map(Cow::Owned).collect(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_type(
|
||||
crate_level: bool,
|
||||
target: Target,
|
||||
cx: &mut AcceptContext<'_, 'sess, S>,
|
||||
) {
|
||||
let is_crate_root = S::id_is_crate_root(cx.target_id);
|
||||
|
||||
if is_crate_root {
|
||||
return;
|
||||
}
|
||||
|
||||
if !crate_level {
|
||||
return;
|
||||
}
|
||||
|
||||
let lint = AttributeLintKind::InvalidStyle {
|
||||
name: cx.attr_path.clone(),
|
||||
is_used_as_inner: cx.attr_style == AttrStyle::Inner,
|
||||
target,
|
||||
target_span: cx.target_span,
|
||||
};
|
||||
let attr_span = cx.attr_span;
|
||||
|
||||
cx.emit_lint(lint, attr_span);
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
|
||||
/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
|
||||
pub(crate) fn allowed_targets_applied(
|
||||
mut allowed_targets: Vec<Target>,
|
||||
target: Target,
|
||||
features: Option<&Features>,
|
||||
) -> (Vec<String>, bool) {
|
||||
// Remove unstable targets from `allowed_targets` if their features are not enabled
|
||||
if let Some(features) = features {
|
||||
if !features.fn_delegation() {
|
||||
allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
|
||||
}
|
||||
if !features.stmt_expr_attributes() {
|
||||
allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
|
||||
}
|
||||
if !features.extern_types() {
|
||||
allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
|
||||
}
|
||||
}
|
||||
|
||||
// We define groups of "similar" targets.
|
||||
// If at least two of the targets are allowed, and the `target` is not in the group,
|
||||
// we collapse the entire group to a single entry to simplify the target list
|
||||
const FUNCTION_LIKE: &[Target] = &[
|
||||
Target::Fn,
|
||||
Target::Closure,
|
||||
Target::ForeignFn,
|
||||
Target::Method(MethodKind::Inherent),
|
||||
Target::Method(MethodKind::Trait { body: false }),
|
||||
Target::Method(MethodKind::Trait { body: true }),
|
||||
Target::Method(MethodKind::TraitImpl),
|
||||
];
|
||||
const METHOD_LIKE: &[Target] = &[
|
||||
Target::Method(MethodKind::Inherent),
|
||||
Target::Method(MethodKind::Trait { body: false }),
|
||||
Target::Method(MethodKind::Trait { body: true }),
|
||||
Target::Method(MethodKind::TraitImpl),
|
||||
];
|
||||
const IMPL_LIKE: &[Target] =
|
||||
&[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
|
||||
const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
|
||||
|
||||
let mut added_fake_targets = Vec::new();
|
||||
filter_targets(
|
||||
&mut allowed_targets,
|
||||
FUNCTION_LIKE,
|
||||
"functions",
|
||||
target,
|
||||
&mut added_fake_targets,
|
||||
);
|
||||
filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
|
||||
filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
|
||||
filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
|
||||
|
||||
// If there is now only 1 target left, show that as the only possible target
|
||||
(
|
||||
added_fake_targets
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(allowed_targets.iter().map(|t| t.plural_name()))
|
||||
.map(|i| i.to_string())
|
||||
.collect(),
|
||||
allowed_targets.len() + added_fake_targets.len() == 1,
|
||||
)
|
||||
}
|
||||
|
||||
fn filter_targets(
|
||||
allowed_targets: &mut Vec<Target>,
|
||||
target_group: &'static [Target],
|
||||
target_group_name: &'static str,
|
||||
target: Target,
|
||||
added_fake_targets: &mut Vec<&'static str>,
|
||||
) {
|
||||
if target_group.contains(&target) {
|
||||
return;
|
||||
}
|
||||
if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
|
||||
return;
|
||||
}
|
||||
allowed_targets.retain(|t| !target_group.contains(t));
|
||||
added_fake_targets.push(target_group_name);
|
||||
}
|
||||
|
||||
/// This is the list of all targets to which a attribute can be applied
|
||||
/// This is used for:
|
||||
/// - `rustc_dummy`, which can be applied to all targets
|
||||
/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
|
||||
pub(crate) const ALL_TARGETS: &'static [Policy] = {
|
||||
use Policy::Allow;
|
||||
&[
|
||||
Allow(Target::ExternCrate),
|
||||
Allow(Target::Use),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Const),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::ForeignMod),
|
||||
Allow(Target::GlobalAsm),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::TraitAlias),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Expression),
|
||||
Allow(Target::Statement),
|
||||
Allow(Target::Arm),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Param),
|
||||
Allow(Target::PatField),
|
||||
Allow(Target::ExprField),
|
||||
Allow(Target::WherePredicate),
|
||||
Allow(Target::MacroCall),
|
||||
Allow(Target::Crate),
|
||||
Allow(Target::Delegation { mac: false }),
|
||||
Allow(Target::Delegation { mac: true }),
|
||||
]
|
||||
};
|
||||
|
|
@ -8,16 +8,16 @@ use rustc_ast::{
|
|||
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
|
||||
Path, Safety,
|
||||
};
|
||||
use rustc_attr_parsing::{AttributeParser, Late};
|
||||
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
|
||||
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
|
||||
use rustc_parse::parse_in;
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE};
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::{errors, parse_in};
|
||||
use crate::{AttributeParser, Late, session_diagnostics as errors};
|
||||
|
||||
pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
|
||||
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
|
||||
|
|
@ -33,7 +33,10 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
|
|||
// Check input tokens for built-in and key-value attributes.
|
||||
match builtin_attr_info {
|
||||
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
|
||||
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
|
||||
Some(BuiltinAttribute { name, template, .. }) => {
|
||||
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
|
||||
return;
|
||||
}
|
||||
match parse_meta(psess, attr) {
|
||||
// Don't check safety again, we just did that
|
||||
Ok(meta) => {
|
||||
|
|
@ -133,16 +136,6 @@ fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
|
|||
});
|
||||
}
|
||||
|
||||
pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
|
||||
if let Delimiter::Parenthesis = delim {
|
||||
return;
|
||||
}
|
||||
psess.dcx().emit_err(errors::CfgAttrBadDelim {
|
||||
span: span.entire(),
|
||||
sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
|
||||
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
|
||||
let is_one_allowed_subword = |items: &[MetaItemInner]| match items {
|
||||
|
|
@ -269,9 +262,6 @@ pub fn check_builtin_meta_item(
|
|||
) {
|
||||
if !is_attr_template_compatible(&template, &meta.kind) {
|
||||
// attrs with new parsers are locally validated so excluded here
|
||||
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
|
||||
return;
|
||||
}
|
||||
emit_malformed_attribute(psess, style, meta.span, name, template);
|
||||
}
|
||||
|
||||
|
|
@ -298,35 +288,42 @@ fn emit_malformed_attribute(
|
|||
suggestions.push(format!("#{inner}[{name}]"));
|
||||
}
|
||||
if let Some(descr) = template.list {
|
||||
suggestions.push(format!("#{inner}[{name}({descr})]"));
|
||||
for descr in descr {
|
||||
suggestions.push(format!("#{inner}[{name}({descr})]"));
|
||||
}
|
||||
}
|
||||
suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
|
||||
if let Some(descr) = template.name_value_str {
|
||||
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
|
||||
for descr in descr {
|
||||
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
|
||||
}
|
||||
}
|
||||
if should_warn(name) {
|
||||
psess.buffer_lint(
|
||||
ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
span,
|
||||
ast::CRATE_NODE_ID,
|
||||
BuiltinLintDiag::IllFormedAttributeInput { suggestions: suggestions.clone() },
|
||||
BuiltinLintDiag::IllFormedAttributeInput {
|
||||
suggestions: suggestions.clone(),
|
||||
docs: template.docs,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
suggestions.sort();
|
||||
psess
|
||||
.dcx()
|
||||
.struct_span_err(span, error_msg)
|
||||
.with_span_suggestions(
|
||||
span,
|
||||
if suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"the following are the possible correct uses"
|
||||
},
|
||||
suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
)
|
||||
.emit();
|
||||
let mut err = psess.dcx().struct_span_err(span, error_msg).with_span_suggestions(
|
||||
span,
|
||||
if suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"the following are the possible correct uses"
|
||||
},
|
||||
suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
if let Some(link) = template.docs {
|
||||
err.note(format!("for more information, visit <{link}>"));
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5,9 +5,8 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
icu_list = "1.2"
|
||||
icu_locid = "1.2"
|
||||
icu_locid_transform = "1.3.2"
|
||||
icu_provider = { version = "1.2", features = ["sync"] }
|
||||
zerovec = "0.10.0"
|
||||
icu_list = { version = "2.0", default-features = false }
|
||||
icu_locale = { version = "2.0", default-features = false, features = ["compiled_data"] }
|
||||
icu_provider = { version = "2.0", features = ["baked", "sync"] }
|
||||
zerovec = "0.11.0"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
// @generated
|
||||
impl_any_provider!(BakedDataProvider);
|
||||
71
compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data
Normal file
71
compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,46 +0,0 @@
|
|||
// @generated
|
||||
/// Marks a type as a data provider. You can then use macros like
|
||||
/// `impl_core_helloworld_v1` to add implementations.
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct MyProvider;
|
||||
/// const _: () = {
|
||||
/// include!("path/to/generated/macros.rs");
|
||||
/// make_provider!(MyProvider);
|
||||
/// impl_core_helloworld_v1!(MyProvider);
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __make_provider {
|
||||
($ name : ty) => {
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl $name {
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub const MUST_USE_MAKE_PROVIDER_MACRO: () = ();
|
||||
}
|
||||
};
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use __make_provider as make_provider;
|
||||
#[macro_use]
|
||||
#[path = "macros/fallback_likelysubtags_v1.data.rs"]
|
||||
mod fallback_likelysubtags_v1;
|
||||
#[doc(inline)]
|
||||
pub use __impl_fallback_likelysubtags_v1 as impl_fallback_likelysubtags_v1;
|
||||
#[macro_use]
|
||||
#[path = "macros/fallback_parents_v1.data.rs"]
|
||||
mod fallback_parents_v1;
|
||||
#[doc(inline)]
|
||||
pub use __impl_fallback_parents_v1 as impl_fallback_parents_v1;
|
||||
#[macro_use]
|
||||
#[path = "macros/fallback_supplement_co_v1.data.rs"]
|
||||
mod fallback_supplement_co_v1;
|
||||
#[doc(inline)]
|
||||
pub use __impl_fallback_supplement_co_v1 as impl_fallback_supplement_co_v1;
|
||||
#[macro_use]
|
||||
#[path = "macros/list_and_v1.data.rs"]
|
||||
mod list_and_v1;
|
||||
#[doc(inline)]
|
||||
pub use __impl_list_and_v1 as impl_list_and_v1;
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// @generated
|
||||
/// Implement `DataProvider<LocaleFallbackLikelySubtagsV1Marker>` on the given struct using the data
|
||||
/// hardcoded in this file. This allows the struct to be used with
|
||||
/// `icu`'s `_unstable` constructors.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_fallback_likelysubtags_v1 {
|
||||
($ provider : ty) => {
|
||||
#[clippy::msrv = "1.66"]
|
||||
const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO;
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl $provider {
|
||||
#[doc(hidden)]
|
||||
pub const SINGLETON_FALLBACK_LIKELYSUBTAGS_V1: &'static <icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker as icu_provider::DataMarker>::Yokeable = &icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1 {
|
||||
l2s: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap::from_parts_unchecked(unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"am\0ar\0as\0be\0bg\0bgcbhobn\0brxchrcv\0doiel\0fa\0gu\0he\0hi\0hy\0ja\0ka\0kk\0km\0kn\0ko\0kokks\0ky\0lo\0maimk\0ml\0mn\0mnimr\0my\0ne\0or\0pa\0ps\0rajru\0sa\0satsd\0si\0sr\0ta\0te\0tg\0th\0ti\0tt\0uk\0ur\0yuezh\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"EthiArabBengCyrlCyrlDevaDevaBengDevaCherCyrlDevaGrekArabGujrHebrDevaArmnJpanGeorCyrlKhmrKndaKoreDevaArabCyrlLaooDevaCyrlMlymCyrlBengDevaMymrDevaOryaGuruArabDevaCyrlDevaOlckArabSinhCyrlTamlTeluCyrlThaiEthiCyrlCyrlArabHantHans") })
|
||||
},
|
||||
lr2s: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap2d::from_parts_unchecked(unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"az\0ha\0kk\0ky\0mn\0ms\0pa\0sd\0sr\0tg\0uz\0yuezh\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"\x03\0\0\0\x05\0\0\0\t\0\0\0\x0B\0\0\0\x0C\0\0\0\r\0\0\0\x0E\0\0\0\x0F\0\0\0\x13\0\0\0\x14\0\0\0\x16\0\0\0\x17\0\0\0&\0\0\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"IQ\0IR\0RU\0CM\0SD\0AF\0CN\0IR\0MN\0CN\0TR\0CN\0CC\0PK\0IN\0ME\0RO\0RU\0TR\0PK\0AF\0CN\0CN\0AU\0BN\0GB\0GF\0HK\0ID\0MO\0PA\0PF\0PH\0SR\0TH\0TW\0US\0VN\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"ArabArabCyrlArabArabArabArabArabArabArabLatnMongArabArabDevaLatnLatnLatnLatnArabArabCyrlHansHantHantHantHantHantHantHantHantHantHantHantHantHantHantHant") })
|
||||
},
|
||||
l2r: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap::from_parts_unchecked(unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"af\0am\0ar\0as\0astaz\0be\0bg\0bgcbhobn\0br\0brxbs\0ca\0cebchrcs\0cv\0cy\0da\0de\0doidsbel\0en\0es\0et\0eu\0fa\0ff\0fi\0filfo\0fr\0ga\0gd\0gl\0gu\0ha\0he\0hi\0hr\0hsbhu\0hy\0ia\0id\0ig\0is\0it\0ja\0jv\0ka\0keakgpkk\0km\0kn\0ko\0kokks\0ky\0lo\0lt\0lv\0maimi\0mk\0ml\0mn\0mnimr\0ms\0my\0ne\0nl\0nn\0no\0or\0pa\0pcmpl\0ps\0pt\0qu\0rajrm\0ro\0ru\0sa\0satsc\0sd\0si\0sk\0sl\0so\0sq\0sr\0su\0sv\0sw\0ta\0te\0tg\0th\0ti\0tk\0to\0tr\0tt\0uk\0ur\0uz\0vi\0wo\0xh\0yo\0yrlyuezh\0zu\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"ZA\0ET\0EG\0IN\0ES\0AZ\0BY\0BG\0IN\0IN\0BD\0FR\0IN\0BA\0ES\0PH\0US\0CZ\0RU\0GB\0DK\0DE\0IN\0DE\0GR\0US\0ES\0EE\0ES\0IR\0SN\0FI\0PH\0FO\0FR\0IE\0GB\0ES\0IN\0NG\0IL\0IN\0HR\0DE\0HU\0AM\x00001ID\0NG\0IS\0IT\0JP\0ID\0GE\0CV\0BR\0KZ\0KH\0IN\0KR\0IN\0IN\0KG\0LA\0LT\0LV\0IN\0NZ\0MK\0IN\0MN\0IN\0IN\0MY\0MM\0NP\0NL\0NO\0NO\0IN\0IN\0NG\0PL\0AF\0BR\0PE\0IN\0CH\0RO\0RU\0IN\0IN\0IT\0PK\0LK\0SK\0SI\0SO\0AL\0RS\0ID\0SE\0TZ\0IN\0IN\0TJ\0TH\0ET\0TM\0TO\0TR\0RU\0UA\0PK\0UZ\0VN\0SN\0ZA\0NG\0BR\0HK\0CN\0ZA\0") })
|
||||
},
|
||||
ls2r: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap2d::from_parts_unchecked(unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"az\0en\0ff\0kk\0ky\0mn\0pa\0sd\0tg\0uz\0yuezh\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"\x01\0\0\0\x02\0\0\0\x03\0\0\0\x04\0\0\0\x06\0\0\0\x07\0\0\0\x08\0\0\0\x0B\0\0\0\x0C\0\0\0\r\0\0\0\x0E\0\0\0\x11\0\0\0") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"ArabShawAdlmArabArabLatnMongArabDevaKhojSindArabArabHansBopoHanbHant") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"IR\0GB\0GN\0CN\0CN\0TR\0CN\0PK\0IN\0IN\0IN\0PK\0AF\0CN\0TW\0TW\0TW\0") })
|
||||
},
|
||||
};
|
||||
}
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl icu_provider::DataProvider<icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker> for $provider {
|
||||
fn load(&self, req: icu_provider::DataRequest) -> Result<icu_provider::DataResponse<icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker>, icu_provider::DataError> {
|
||||
if req.locale.is_empty() { Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(Self::SINGLETON_FALLBACK_LIKELYSUBTAGS_V1)), metadata: Default::default() }) } else { Err(icu_provider::DataErrorKind::ExtraneousLocale.with_req(<icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker as icu_provider::KeyedDataMarker>::KEY, req)) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// @generated
|
||||
/// Implement `DataProvider<LocaleFallbackParentsV1Marker>` on the given struct using the data
|
||||
/// hardcoded in this file. This allows the struct to be used with
|
||||
/// `icu`'s `_unstable` constructors.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_fallback_parents_v1 {
|
||||
($ provider : ty) => {
|
||||
#[clippy::msrv = "1.66"]
|
||||
const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO;
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl $provider {
|
||||
#[doc(hidden)]
|
||||
pub const SINGLETON_FALLBACK_PARENTS_V1: &'static <icu_locid_transform::provider::LocaleFallbackParentsV1Marker as icu_provider::DataMarker>::Yokeable = &icu_locid_transform::provider::LocaleFallbackParentsV1 {
|
||||
parents: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap::from_parts_unchecked(unsafe { zerovec::VarZeroVec::from_bytes_unchecked(b"\x84\0\0\0\0\0\x06\0\x0B\0\x10\0\x15\0\x1A\0\x1F\0$\0)\0.\x003\08\0=\0B\0G\0L\0Q\0V\0[\0`\0e\0j\0o\0t\0y\0~\0\x83\0\x88\0\x8D\0\x92\0\x97\0\x9C\0\xA1\0\xA6\0\xAB\0\xB0\0\xB5\0\xBA\0\xBF\0\xC4\0\xC9\0\xCE\0\xD3\0\xD8\0\xDD\0\xE2\0\xE7\0\xEC\0\xF1\0\xF6\0\xFB\0\0\x01\x05\x01\n\x01\x0F\x01\x14\x01\x19\x01\x1E\x01#\x01(\x01-\x012\x017\x01<\x01A\x01F\x01K\x01P\x01U\x01Z\x01_\x01d\x01i\x01n\x01s\x01x\x01}\x01\x82\x01\x87\x01\x8C\x01\x91\x01\x96\x01\x9B\x01\xA0\x01\xA5\x01\xAA\x01\xAF\x01\xB4\x01\xB9\x01\xBE\x01\xC3\x01\xC8\x01\xCD\x01\xD2\x01\xD7\x01\xDC\x01\xE1\x01\xE6\x01\xEB\x01\xF0\x01\xF5\x01\xFA\x01\xFF\x01\x04\x02\t\x02\x0E\x02\x13\x02\x18\x02\x1D\x02\"\x02'\x02,\x021\x026\x02;\x02@\x02G\x02I\x02K\x02M\x02R\x02W\x02\\\x02a\x02f\x02k\x02p\x02u\x02z\x02\x7F\x02\x84\x02\x89\x02en-150en-AGen-AIen-ATen-AUen-BBen-BEen-BMen-BSen-BWen-BZen-CCen-CHen-CKen-CMen-CXen-CYen-DEen-DGen-DKen-DMen-ERen-FIen-FJen-FKen-FMen-GBen-GDen-GGen-GHen-GIen-GMen-GYen-HKen-IEen-ILen-IMen-INen-IOen-JEen-JMen-KEen-KIen-KNen-KYen-LCen-LRen-LSen-MGen-MOen-MSen-MTen-MUen-MVen-MWen-MYen-NAen-NFen-NGen-NLen-NRen-NUen-NZen-PGen-PKen-PNen-PWen-RWen-SBen-SCen-SDen-SEen-SGen-SHen-SIen-SLen-SSen-SXen-SZen-TCen-TKen-TOen-TTen-TVen-TZen-UGen-VCen-VGen-VUen-WSen-ZAen-ZMen-ZWes-ARes-BOes-BRes-BZes-CLes-COes-CRes-CUes-DOes-ECes-GTes-HNes-MXes-NIes-PAes-PEes-PRes-PYes-SVes-USes-UYes-VEhi-Latnhtnbnnno-NOpt-AOpt-CHpt-CVpt-FRpt-GQpt-GWpt-LUpt-MOpt-MZpt-STpt-TLzh-Hant-MO") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01150en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001en\0\0\0\0\0\0\x01001es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419es\0\0\0\0\0\0\x01419en\0\0\0\0\0\0\x01IN\0fr\0\0\0\0\0\0\x01HT\0no\0\0\0\0\0\0\0\0\0\0no\0\0\0\0\0\0\0\0\0\0no\0\0\0\0\0\0\0\0\0\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0pt\0\0\0\0\0\0\x01PT\0zh\0\x01Hant\x01HK\0") })
|
||||
},
|
||||
};
|
||||
}
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl icu_provider::DataProvider<icu_locid_transform::provider::LocaleFallbackParentsV1Marker> for $provider {
|
||||
fn load(&self, req: icu_provider::DataRequest) -> Result<icu_provider::DataResponse<icu_locid_transform::provider::LocaleFallbackParentsV1Marker>, icu_provider::DataError> {
|
||||
if req.locale.is_empty() { Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(Self::SINGLETON_FALLBACK_PARENTS_V1)), metadata: Default::default() }) } else { Err(icu_provider::DataErrorKind::ExtraneousLocale.with_req(<icu_locid_transform::provider::LocaleFallbackParentsV1Marker as icu_provider::KeyedDataMarker>::KEY, req)) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// @generated
|
||||
/// Implement `DataProvider<CollationFallbackSupplementV1Marker>` on the given struct using the data
|
||||
/// hardcoded in this file. This allows the struct to be used with
|
||||
/// `icu`'s `_unstable` constructors.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_fallback_supplement_co_v1 {
|
||||
($ provider : ty) => {
|
||||
#[clippy::msrv = "1.66"]
|
||||
const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO;
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl $provider {
|
||||
#[doc(hidden)]
|
||||
pub const SINGLETON_FALLBACK_SUPPLEMENT_CO_V1: &'static <icu_locid_transform::provider::CollationFallbackSupplementV1Marker as icu_provider::DataMarker>::Yokeable = &icu_locid_transform::provider::LocaleFallbackSupplementV1 {
|
||||
parents: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap::from_parts_unchecked(unsafe { zerovec::VarZeroVec::from_bytes_unchecked(b"\x01\0\0\0\0\0yue") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"zh\0\x01Hant\0\0\0\0") })
|
||||
},
|
||||
unicode_extension_defaults: unsafe {
|
||||
#[allow(unused_unsafe)]
|
||||
zerovec::ZeroMap2d::from_parts_unchecked(unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"co") }, unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"\x02\0\0\0") }, unsafe { zerovec::VarZeroVec::from_bytes_unchecked(b"\x02\0\0\0\0\0\x02\0zhzh-Hant") }, unsafe { zerovec::VarZeroVec::from_bytes_unchecked(b"\x02\0\0\0\0\0\x06\0pinyinstroke") })
|
||||
},
|
||||
};
|
||||
}
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl icu_provider::DataProvider<icu_locid_transform::provider::CollationFallbackSupplementV1Marker> for $provider {
|
||||
fn load(&self, req: icu_provider::DataRequest) -> Result<icu_provider::DataResponse<icu_locid_transform::provider::CollationFallbackSupplementV1Marker>, icu_provider::DataError> {
|
||||
if req.locale.is_empty() { Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(Self::SINGLETON_FALLBACK_SUPPLEMENT_CO_V1)), metadata: Default::default() }) } else { Err(icu_provider::DataErrorKind::ExtraneousLocale.with_req(<icu_locid_transform::provider::CollationFallbackSupplementV1Marker as icu_provider::KeyedDataMarker>::KEY, req)) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,31 +1,40 @@
|
|||
// @generated
|
||||
include!("macros.rs");
|
||||
include!("list_and_v1.rs.data");
|
||||
/// Marks a type as a data provider. You can then use macros like
|
||||
/// `impl_core_helloworld_v1` to add implementations.
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct MyProvider;
|
||||
/// const _: () = {
|
||||
/// include!("path/to/generated/macros.rs");
|
||||
/// make_provider!(MyProvider);
|
||||
/// impl_core_helloworld_v1!(MyProvider);
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __make_provider {
|
||||
($ name : ty) => {
|
||||
#[clippy::msrv = "1.82"]
|
||||
impl $name {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const MUST_USE_MAKE_PROVIDER_MACRO: () = ();
|
||||
}
|
||||
icu_provider::marker::impl_data_provider_never_marker!($name);
|
||||
};
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use __make_provider as make_provider;
|
||||
/// This macro requires the following crates:
|
||||
/// * `icu_list`
|
||||
/// * `icu_locale/compiled_data`
|
||||
/// * `icu_provider`
|
||||
/// * `icu_provider/baked`
|
||||
/// * `zerovec`
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_data_provider {
|
||||
($ provider : ty) => {
|
||||
make_provider!($provider);
|
||||
impl_fallback_likelysubtags_v1!($provider);
|
||||
impl_fallback_parents_v1!($provider);
|
||||
impl_fallback_supplement_co_v1!($provider);
|
||||
impl_list_and_v1!($provider);
|
||||
};
|
||||
}
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_any_provider {
|
||||
($ provider : ty) => {
|
||||
#[clippy::msrv = "1.66"]
|
||||
impl icu_provider::AnyProvider for $provider {
|
||||
fn load_any(&self, key: icu_provider::DataKey, req: icu_provider::DataRequest) -> Result<icu_provider::AnyResponse, icu_provider::DataError> {
|
||||
match key.hashed() {
|
||||
h if h == <icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker as icu_provider::KeyedDataMarker>::KEY.hashed() => icu_provider::DataProvider::<icu_locid_transform::provider::LocaleFallbackLikelySubtagsV1Marker>::load(self, req).map(icu_provider::DataResponse::wrap_into_any_response),
|
||||
h if h == <icu_locid_transform::provider::LocaleFallbackParentsV1Marker as icu_provider::KeyedDataMarker>::KEY.hashed() => icu_provider::DataProvider::<icu_locid_transform::provider::LocaleFallbackParentsV1Marker>::load(self, req).map(icu_provider::DataResponse::wrap_into_any_response),
|
||||
h if h == <icu_locid_transform::provider::CollationFallbackSupplementV1Marker as icu_provider::KeyedDataMarker>::KEY.hashed() => icu_provider::DataProvider::<icu_locid_transform::provider::CollationFallbackSupplementV1Marker>::load(self, req).map(icu_provider::DataResponse::wrap_into_any_response),
|
||||
h if h == <icu_list::provider::AndListV1Marker as icu_provider::KeyedDataMarker>::KEY.hashed() => icu_provider::DataProvider::<icu_list::provider::AndListV1Marker>::load(self, req).map(icu_provider::DataResponse::wrap_into_any_response),
|
||||
_ => Err(icu_provider::DataErrorKind::MissingDataKey.with_req(key, req)),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#[clippy::msrv = "1.66"]
|
||||
pub struct BakedDataProvider;
|
||||
impl_data_provider!(BakedDataProvider);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@
|
|||
//! To regenerate the data, run this command:
|
||||
//!
|
||||
//! ```text
|
||||
//! icu4x-datagen -W --pretty --fingerprint --use-separate-crates \
|
||||
//! --format mod -l en es fr it ja pt ru tr zh zh-Hans zh-Hant \
|
||||
//! -k list/and@1 fallback/likelysubtags@1 fallback/parents@1 fallback/supplement/co@1 \
|
||||
//! --cldr-tag latest --icuexport-tag latest -o src/data
|
||||
//! icu4x-datagen -W --pretty --use-separate-crates \
|
||||
//! --format baked --locales @en @es @fr @it @ja @pt @ru @tr @zh @zh-Hans @zh-Hant \
|
||||
//! -m ListAndV1 -o src/data
|
||||
//! ```
|
||||
|
||||
// tidy-alphabetical-start
|
||||
|
|
@ -29,26 +28,26 @@
|
|||
// #![warn(unreachable_pub)] // don't use because this crate is mostly generated code
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod data {
|
||||
include!("data/mod.rs");
|
||||
include!("data/any.rs");
|
||||
}
|
||||
pub struct BakedDataProvider;
|
||||
|
||||
pub use data::BakedDataProvider;
|
||||
include!("data/mod.rs");
|
||||
const _: () = {
|
||||
impl_data_provider!(BakedDataProvider);
|
||||
};
|
||||
|
||||
pub const fn baked_data_provider() -> BakedDataProvider {
|
||||
data::BakedDataProvider
|
||||
BakedDataProvider
|
||||
}
|
||||
|
||||
pub mod supported_locales {
|
||||
pub const EN: icu_locid::Locale = icu_locid::locale!("en");
|
||||
pub const ES: icu_locid::Locale = icu_locid::locale!("es");
|
||||
pub const FR: icu_locid::Locale = icu_locid::locale!("fr");
|
||||
pub const IT: icu_locid::Locale = icu_locid::locale!("it");
|
||||
pub const JA: icu_locid::Locale = icu_locid::locale!("ja");
|
||||
pub const PT: icu_locid::Locale = icu_locid::locale!("pt");
|
||||
pub const RU: icu_locid::Locale = icu_locid::locale!("ru");
|
||||
pub const TR: icu_locid::Locale = icu_locid::locale!("tr");
|
||||
pub const ZH_HANS: icu_locid::Locale = icu_locid::locale!("zh-Hans");
|
||||
pub const ZH_HANT: icu_locid::Locale = icu_locid::locale!("zh-Hant");
|
||||
pub const EN: icu_locale::Locale = icu_locale::locale!("en");
|
||||
pub const ES: icu_locale::Locale = icu_locale::locale!("es");
|
||||
pub const FR: icu_locale::Locale = icu_locale::locale!("fr");
|
||||
pub const IT: icu_locale::Locale = icu_locale::locale!("it");
|
||||
pub const JA: icu_locale::Locale = icu_locale::locale!("ja");
|
||||
pub const PT: icu_locale::Locale = icu_locale::locale!("pt");
|
||||
pub const RU: icu_locale::Locale = icu_locale::locale!("ru");
|
||||
pub const TR: icu_locale::Locale = icu_locale::locale!("tr");
|
||||
pub const ZH_HANS: icu_locale::Locale = icu_locale::locale!("zh-Hans");
|
||||
pub const ZH_HANT: icu_locale::Locale = icu_locale::locale!("zh-Hant");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error =
|
|||
lifetime may not live long enough
|
||||
|
||||
borrowck_limitations_implies_static =
|
||||
due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
due to a current limitation of the type system, this implies a `'static` lifetime
|
||||
|
||||
borrowck_move_closure_suggestion =
|
||||
consider adding 'move' keyword before the nested closure
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub use super::polonius::legacy::{
|
|||
RichLocation, RustcFacts,
|
||||
};
|
||||
pub use super::region_infer::RegionInferenceContext;
|
||||
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
|
||||
use crate::BorrowCheckRootCtxt;
|
||||
|
||||
/// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
|
||||
/// its nested bodies.
|
||||
|
|
@ -127,13 +127,6 @@ pub fn get_bodies_with_borrowck_facts(
|
|||
) -> FxHashMap<LocalDefId, BodyWithBorrowckFacts<'_>> {
|
||||
let mut root_cx =
|
||||
BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options)));
|
||||
|
||||
// See comment in `rustc_borrowck::mir_borrowck`
|
||||
let nested_bodies = tcx.nested_bodies_within(root_def_id);
|
||||
for def_id in nested_bodies {
|
||||
root_cx.get_or_insert_nested(def_id);
|
||||
}
|
||||
|
||||
do_mir_borrowck(&mut root_cx, root_def_id);
|
||||
root_cx.do_mir_borrowck();
|
||||
root_cx.consumer.unwrap().bodies
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph;
|
||||
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
|
||||
|
|
@ -317,9 +316,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
|||
loans_out_of_scope_at_location: FxIndexMap::default(),
|
||||
};
|
||||
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
|
||||
let issuing_region = loan_data.region;
|
||||
let loan_issued_at = loan_data.reserve_location;
|
||||
prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
|
||||
prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
|
||||
}
|
||||
|
||||
prec.loans_out_of_scope_at_location
|
||||
|
|
@ -328,45 +326,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
|||
/// Loans are in scope while they are live: whether they are contained within any live region.
|
||||
/// In the location-insensitive analysis, a loan will be contained in a region if the issuing
|
||||
/// region can reach it in the subset graph. So this is a reachability problem.
|
||||
fn precompute_loans_out_of_scope(
|
||||
&mut self,
|
||||
loan_idx: BorrowIndex,
|
||||
issuing_region: RegionVid,
|
||||
loan_issued_at: Location,
|
||||
) {
|
||||
let sccs = self.regioncx.constraint_sccs();
|
||||
let universal_regions = self.regioncx.universal_regions();
|
||||
|
||||
// The loop below was useful for the location-insensitive analysis but shouldn't be
|
||||
// impactful in the location-sensitive case. It seems that it does, however, as without it a
|
||||
// handful of tests fail. That likely means some liveness or outlives data related to choice
|
||||
// regions is missing
|
||||
// FIXME: investigate the impact of loans traversing applied member constraints and why some
|
||||
// tests fail otherwise.
|
||||
//
|
||||
// We first handle the cases where the loan doesn't go out of scope, depending on the
|
||||
// issuing region's successors.
|
||||
for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
|
||||
// Via applied member constraints
|
||||
//
|
||||
// The issuing region can flow into the choice regions, and they are either:
|
||||
// - placeholders or free regions themselves,
|
||||
// - or also transitively outlive a free region.
|
||||
//
|
||||
// That is to say, if there are applied member constraints here, the loan escapes the
|
||||
// function and cannot go out of scope. We could early return here.
|
||||
//
|
||||
// For additional insurance via fuzzing and crater, we verify that the constraint's min
|
||||
// choice indeed escapes the function. In the future, we could e.g. turn this check into
|
||||
// a debug assert and early return as an optimization.
|
||||
let scc = sccs.scc(successor);
|
||||
for constraint in self.regioncx.applied_member_constraints(scc) {
|
||||
if universal_regions.is_universal_region(constraint.min_choice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
|
||||
let first_block = loan_issued_at.block;
|
||||
let first_bb_data = &self.body.basic_blocks[first_block];
|
||||
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
|
||||
let elaborated_args =
|
||||
std::iter::zip(*args, &generics.own_params).map(|(arg, param)| {
|
||||
if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) {
|
||||
if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) {
|
||||
let default = tcx.object_lifetime_default(param.def_id);
|
||||
|
||||
let re_static = tcx.lifetimes.re_static;
|
||||
|
|
@ -464,7 +464,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
|
||||
has_dyn = true;
|
||||
|
||||
Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into()
|
||||
Ty::new_dynamic(tcx, obj, implied_region).into()
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
|
|
@ -565,7 +565,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
|
||||
borrow_region,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
|
||||
outlived_region,
|
||||
);
|
||||
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
||||
use rustc_hir::{
|
||||
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
|
||||
};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
|
||||
use rustc_infer::traits::SelectionError;
|
||||
|
|
@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
|
||||
/// implicitly introduce an "outlives `'static`" constraint.
|
||||
///
|
||||
/// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
|
||||
/// note for failed type tests instead of outlives errors.
|
||||
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_, G>,
|
||||
diag: &mut Diag<'_, G>,
|
||||
path: &[OutlivesConstraint<'tcx>],
|
||||
) {
|
||||
let predicate_span = path.iter().find_map(|constraint| {
|
||||
let tcx = self.infcx.tcx;
|
||||
let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
|
||||
let outlived = constraint.sub;
|
||||
if let Some(origin) = self.regioncx.definitions.get(outlived)
|
||||
&& let NllRegionVariableOrigin::Placeholder(_) = origin.origin
|
||||
&& let ConstraintCategory::Predicate(span) = constraint.category
|
||||
&& let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
|
||||
&& let Some(id) = placeholder.bound.kind.get_id()
|
||||
&& let Some(placeholder_id) = id.as_local()
|
||||
&& let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
|
||||
&& let Some(generics_impl) =
|
||||
tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
|
||||
{
|
||||
Some(span)
|
||||
Some((gat_hir_id, generics_impl))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(span) = predicate_span {
|
||||
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
|
||||
// Look for the where-bound which introduces the placeholder.
|
||||
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
|
||||
// and `T: for<'a> Trait`<'a>.
|
||||
for pred in generics.predicates {
|
||||
let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
|
||||
bound_generic_params,
|
||||
bounds,
|
||||
..
|
||||
}) = pred.kind
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
|
||||
return;
|
||||
}
|
||||
for bound in bounds.iter() {
|
||||
if let GenericBound::Trait(bound) = bound {
|
||||
if bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ use rustc_middle::bug;
|
|||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
|
||||
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
|
||||
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use tracing::debug;
|
||||
|
|
@ -507,12 +508,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
);
|
||||
|
||||
let closure_span = tcx.def_span(def_id);
|
||||
|
||||
self.cannot_move_out_of(span, &place_description)
|
||||
.with_span_label(upvar_span, "captured outer variable")
|
||||
.with_span_label(
|
||||
closure_span,
|
||||
format!("captured by this `{closure_kind}` closure"),
|
||||
)
|
||||
.with_span_help(
|
||||
self.get_closure_bound_clause_span(*def_id),
|
||||
"`Fn` and `FnMut` closures require captured values to be able to be \
|
||||
consumed multiple times, but `FnOnce` closures may consume them only once",
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let source = self.borrowed_content_source(deref_base);
|
||||
|
|
@ -561,6 +568,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
err
|
||||
}
|
||||
|
||||
fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
|
||||
let tcx = self.infcx.tcx;
|
||||
let typeck_result = tcx.typeck(self.mir_def_id());
|
||||
// Check whether the closure is an argument to a call, if so,
|
||||
// get the instantiated where-bounds of that call.
|
||||
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
|
||||
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
|
||||
|
||||
let predicates = match parent.kind {
|
||||
hir::ExprKind::Call(callee, _) => {
|
||||
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
|
||||
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
|
||||
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
|
||||
}
|
||||
hir::ExprKind::MethodCall(..) => {
|
||||
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
|
||||
return DUMMY_SP;
|
||||
};
|
||||
let args = typeck_result.node_args(parent.hir_id);
|
||||
tcx.predicates_of(method).instantiate(tcx, args)
|
||||
}
|
||||
_ => return DUMMY_SP,
|
||||
};
|
||||
|
||||
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
|
||||
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
|
||||
if let Some(clause) = pred.as_trait_clause()
|
||||
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
|
||||
&& *clause_closure_def_id == def_id
|
||||
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
|
||||
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
|
||||
{
|
||||
// Found `<TyOfCapturingClosure as FnMut>`
|
||||
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
|
||||
// could be changed to `FnOnce()` to avoid the move error.
|
||||
return *span;
|
||||
}
|
||||
}
|
||||
DUMMY_SP
|
||||
}
|
||||
|
||||
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
|
||||
match error {
|
||||
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use either::Either;
|
||||
use hir::{ExprKind, Param};
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
|
|
@ -12,15 +13,16 @@ use rustc_middle::bug;
|
|||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::visit::PlaceContext;
|
||||
use rustc_middle::mir::{
|
||||
self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
|
||||
PlaceRef, ProjectionElem,
|
||||
self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
|
||||
Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
|
||||
StatementKind, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
|
||||
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::diagnostics::BorrowedContentSource;
|
||||
use crate::{MirBorrowckCtxt, session_diagnostics};
|
||||
|
|
@ -31,6 +33,33 @@ pub(crate) enum AccessKind {
|
|||
Mutate,
|
||||
}
|
||||
|
||||
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
|
||||
/// locations.
|
||||
fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(body);
|
||||
visitor.locations
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn report_mutability_error(
|
||||
&mut self,
|
||||
|
|
@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Also suggest adding mut for upvars
|
||||
// Also suggest adding mut for upvars.
|
||||
PlaceRef {
|
||||
local,
|
||||
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
|
||||
|
|
@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// complete hack to approximate old AST-borrowck
|
||||
// diagnostic: if the span starts with a mutable borrow of
|
||||
// a local variable, then just suggest the user remove it.
|
||||
// Complete hack to approximate old AST-borrowck diagnostic: if the span starts
|
||||
// with a mutable borrow of a local variable, then just suggest the user remove it.
|
||||
PlaceRef { local: _, projection: [] }
|
||||
if self
|
||||
.infcx
|
||||
|
|
@ -677,17 +705,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
/// - is the trait from the local crate? If not, we can't suggest changing signatures
|
||||
/// - `Span` of the argument in the trait definition
|
||||
fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
|
||||
let tcx = self.infcx.tcx;
|
||||
if self.body.local_kind(local) != LocalKind::Arg {
|
||||
return (false, false, None);
|
||||
}
|
||||
let my_def = self.body.source.def_id();
|
||||
let Some(td) =
|
||||
self.infcx.tcx.impl_of_assoc(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
|
||||
tcx.trait_impl_of_assoc(my_def).and_then(|id| self.infcx.tcx.trait_id_of_impl(id))
|
||||
else {
|
||||
return (false, false, None);
|
||||
};
|
||||
|
||||
let implemented_trait_item = self.infcx.tcx.associated_item(my_def).trait_item_def_id;
|
||||
let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
|
||||
|
||||
(
|
||||
true,
|
||||
|
|
@ -768,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
// point to span of upvar making closure call require mutable borrow
|
||||
// Point to span of upvar making closure call that requires a mutable borrow
|
||||
fn show_mutating_upvar(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
|
|
@ -824,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
} else {
|
||||
bug!("not an upvar")
|
||||
};
|
||||
// sometimes we deliberately don't store the name of a place when coming from a macro in
|
||||
// Sometimes we deliberately don't store the name of a place when coming from a macro in
|
||||
// another crate. We generally want to limit those diagnostics a little, to hide
|
||||
// implementation details (such as those from pin!() or format!()). In that case show a
|
||||
// slightly different error message, or none at all if something else happened. In other
|
||||
|
|
@ -935,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
|
||||
let mut look_at_return = true;
|
||||
|
||||
// If the HIR node is a function or method call gets the def ID
|
||||
// of the called function or method and the span and args of the call expr
|
||||
// If the HIR node is a function or method call, get the DefId
|
||||
// of the callee function or method, the span, and args of the call expr
|
||||
let get_call_details = || {
|
||||
let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
|
||||
return None;
|
||||
|
|
@ -1050,7 +1079,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let mut cur_expr = expr;
|
||||
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
|
||||
if path_segment.ident.name == sym::iter {
|
||||
// check `_ty` has `iter_mut` method
|
||||
// Check that the type has an `iter_mut` method.
|
||||
let res = self
|
||||
.infcx
|
||||
.tcx
|
||||
|
|
@ -1080,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
|
||||
/// locations.
|
||||
fn find_assignments(&self, local: Local) -> Vec<Location> {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(
|
||||
&mut self,
|
||||
local: Local,
|
||||
place_context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(self.body);
|
||||
visitor.locations
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
|
||||
|
|
@ -1121,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
|
||||
|
||||
if is_trait_sig && !is_local {
|
||||
// Do not suggest to change the signature when the trait comes from another crate.
|
||||
// Do not suggest changing the signature when the trait comes from another crate.
|
||||
err.span_label(
|
||||
local_decl.source_info.span,
|
||||
format!("this is an immutable {pointer_desc}"),
|
||||
|
|
@ -1130,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
let decl_span = local_decl.source_info.span;
|
||||
|
||||
let amp_mut_sugg = match *local_decl.local_info() {
|
||||
let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
|
||||
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
|
||||
(AmpMutSugg::Type { span, suggestion, additional }, None)
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
|
|
@ -1142,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
opt_ty_info,
|
||||
..
|
||||
})) => {
|
||||
// check if the RHS is from desugaring
|
||||
// Check if the RHS is from desugaring.
|
||||
let first_assignment = find_assignments(&self.body, local).first().copied();
|
||||
let first_assignment_stmt = first_assignment
|
||||
.and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
|
||||
trace!(?first_assignment_stmt);
|
||||
let opt_assignment_rhs_span =
|
||||
self.find_assignments(local).first().map(|&location| {
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (
|
||||
_,
|
||||
mir::Rvalue::Use(mir::Operand::Copy(place)),
|
||||
)),
|
||||
..
|
||||
}) = self.body[location.block].statements.get(location.statement_index)
|
||||
{
|
||||
self.body.local_decls[place.local].source_info.span
|
||||
} else {
|
||||
self.body.source_info(location).span
|
||||
}
|
||||
});
|
||||
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
|
||||
// on for loops, RHS points to the iterator part
|
||||
Some(DesugaringKind::ForLoop) => {
|
||||
let span = opt_assignment_rhs_span.unwrap();
|
||||
self.suggest_similar_mut_method_for_for_loop(err, span);
|
||||
first_assignment.map(|loc| self.body.source_info(loc).span);
|
||||
let mut source_span = opt_assignment_rhs_span;
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
|
||||
..
|
||||
}) = first_assignment_stmt
|
||||
{
|
||||
let local_span = self.body.local_decls[place.local].source_info.span;
|
||||
// `&self` in async functions have a `desugaring_kind`, but the local we assign
|
||||
// it with does not, so use the local_span for our checks later.
|
||||
source_span = Some(local_span);
|
||||
if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
|
||||
// On for loops, RHS points to the iterator part.
|
||||
self.suggest_similar_mut_method_for_for_loop(err, local_span);
|
||||
err.span_label(
|
||||
span,
|
||||
local_span,
|
||||
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
|
||||
);
|
||||
None
|
||||
}
|
||||
// don't create labels for compiler-generated spans
|
||||
Some(_) => None,
|
||||
// don't create labels for the span not from user's code
|
||||
None if opt_assignment_rhs_span
|
||||
.is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
|
||||
{
|
||||
None
|
||||
}
|
||||
None => {
|
||||
if name != kw::SelfLower {
|
||||
suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
)
|
||||
} else {
|
||||
match local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
opt_ty_info: None,
|
||||
..
|
||||
})) => {
|
||||
let (span, sugg) =
|
||||
suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: sugg,
|
||||
additional: None,
|
||||
})
|
||||
}
|
||||
// explicit self (eg `self: &'a Self`)
|
||||
_ => suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
),
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't create labels for compiler-generated spans or spans not from users' code.
|
||||
if source_span.is_some_and(|s| {
|
||||
s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This could be because we're in an `async fn`.
|
||||
if name == kw::SelfLower && opt_ty_info.is_none() {
|
||||
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
(AmpMutSugg::Type { span, suggestion, additional: None }, None)
|
||||
} else if let Some(sugg) =
|
||||
suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
|
||||
{
|
||||
(sugg, opt_ty_info)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
|
|
@ -1222,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
..
|
||||
})) => {
|
||||
let pattern_span: Span = local_decl.source_info.span;
|
||||
suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: "mut ".to_owned(),
|
||||
additional: None,
|
||||
})
|
||||
let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
|
||||
return;
|
||||
};
|
||||
(AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match amp_mut_sugg {
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span: err_help_span,
|
||||
suggestion: suggested_code,
|
||||
additional,
|
||||
}) => {
|
||||
let mut sugg = vec![(err_help_span, suggested_code)];
|
||||
if let Some(s) = additional {
|
||||
sugg.push(s);
|
||||
}
|
||||
let mut suggest = |suggs: Vec<_>, applicability, extra| {
|
||||
if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
|
||||
{
|
||||
err.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"consider changing this to be a mutable {pointer_desc}{}",
|
||||
if is_trait_sig {
|
||||
" in the `impl` method and the `trait` definition"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
err.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"consider changing this to be a mutable {pointer_desc}{}{extra}",
|
||||
if is_trait_sig {
|
||||
" in the `impl` method and the `trait` definition"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
suggs,
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
|
||||
let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
|
||||
AmpMutSugg::Type { span, suggestion, additional } => {
|
||||
let mut sugg = vec![(span, suggestion)];
|
||||
sugg.extend(additional);
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
AmpMutSugg::MapGetMut { span, suggestion } => {
|
||||
if self.infcx.tcx.sess.source_map().is_imported(span) {
|
||||
return;
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider using `get_mut`",
|
||||
vec![(span, suggestion)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return;
|
||||
}
|
||||
AmpMutSugg::Expr { span, suggestion } => {
|
||||
// `Expr` suggestions should change type annotations if they already exist (probably immut),
|
||||
// but do not add new type annotations.
|
||||
(vec![(span, suggestion)], false)
|
||||
}
|
||||
AmpMutSugg::ChangeBinding => (vec![], true),
|
||||
};
|
||||
|
||||
// Find a binding's type to make mutable.
|
||||
let (binding_exists, span) = match local_var_ty_info {
|
||||
// If this is a variable binding with an explicit type,
|
||||
// then we will suggest changing it to be mutable.
|
||||
// This is `Applicability::MachineApplicable`.
|
||||
Some(ty_span) => (true, ty_span),
|
||||
|
||||
// Otherwise, we'll suggest *adding* an annotated type, we'll suggest
|
||||
// the RHS's type for that.
|
||||
// This is `Applicability::HasPlaceholders`.
|
||||
None => (false, decl_span),
|
||||
};
|
||||
|
||||
if !binding_exists && !add_type_annotation_if_not_exists {
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the binding already exists and is a reference with an explicit
|
||||
// lifetime, then we can suggest adding ` mut`. This is special-cased from
|
||||
// the path without an explicit lifetime.
|
||||
let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& src.starts_with("&'")
|
||||
// Note that `&' a T` is invalid so this is correct.
|
||||
&& let Some(ws_pos) = src.find(char::is_whitespace)
|
||||
{
|
||||
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
|
||||
(span, " mut".to_owned(), true)
|
||||
// If there is already a binding, we modify it to be `mut`.
|
||||
} else if binding_exists {
|
||||
// Shrink the span to just after the `&` in `&variable`.
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
(span, "mut ".to_owned(), true)
|
||||
} else {
|
||||
// Otherwise, suggest that the user annotates the binding; We provide the
|
||||
// type of the local.
|
||||
let ty = local_decl.ty.builtin_deref(true).unwrap();
|
||||
|
||||
(span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
|
||||
};
|
||||
|
||||
if suggest_now {
|
||||
// Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
|
||||
let has_change = !sugg.is_empty();
|
||||
sugg.push((sugg_span, sugg_str));
|
||||
suggest(
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
// FIXME(fee1-dead) this somehow doesn't fire
|
||||
if has_change { " and changing the binding's type" } else { "" },
|
||||
);
|
||||
return;
|
||||
} else if !sugg.is_empty() {
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
BindingFinder { span: sugg_span }.visit_body(&body).break_value()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
|
||||
|
||||
let Some(hir::Node::LetStmt(local)) = node else {
|
||||
err.span_label(
|
||||
sugg_span,
|
||||
format!("consider changing this binding's type to be: `{sugg_str}`"),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// FIXME: This error message isn't useful, since we're just
|
||||
// vaguely suggesting to clone a value that already
|
||||
// implements `Clone`.
|
||||
//
|
||||
// A correct suggestion here would take into account the fact
|
||||
// that inference may be affected by missing types on bindings,
|
||||
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
|
||||
// example.
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.infcx.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.upcast(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: false, span: err_label_span, suggestion: message, ..
|
||||
}) => {
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
BindingFinder { span: err_label_span }.visit_body(&body).break_value()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
|
||||
{
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.type_implements_trait_shallow(
|
||||
clone_trait,
|
||||
ty.peel_refs(),
|
||||
self.infcx.param_env,
|
||||
)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// FIXME: This error message isn't useful, since we're just
|
||||
// vaguely suggesting to clone a value that already
|
||||
// implements `Clone`.
|
||||
//
|
||||
// A correct suggestion here would take into account the fact
|
||||
// that inference may be affected by missing types on bindings,
|
||||
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
|
||||
// example.
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.infcx.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.upcast(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
implementations trait bounds could not be met, so \
|
||||
this call clones the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::Select(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::Select(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
||||
error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => {
|
||||
("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
|
||||
}
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
format!("consider changing this binding's type to be: `{message}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, sugg_str),
|
||||
None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1463,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
|
|||
}
|
||||
}
|
||||
|
||||
struct AmpMutSugg {
|
||||
has_sugg: bool,
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
additional: Option<(Span, String)>,
|
||||
enum AmpMutSugg {
|
||||
/// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
|
||||
/// `ref x` to `ref mut x`, etc.
|
||||
Type {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
additional: Option<(Span, String)>,
|
||||
},
|
||||
/// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
|
||||
Expr {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
},
|
||||
/// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
|
||||
MapGetMut {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
},
|
||||
ChangeBinding,
|
||||
}
|
||||
|
||||
// When we want to suggest a user change a local variable to be a `&mut`, there
|
||||
|
|
@ -1486,110 +1529,111 @@ struct AmpMutSugg {
|
|||
// This implementation attempts to emulate AST-borrowck prioritization
|
||||
// by trying (3.), then (2.) and finally falling back on (1.).
|
||||
fn suggest_ampmut<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
decl_ty: Ty<'tcx>,
|
||||
decl_span: Span,
|
||||
opt_assignment_rhs_span: Option<Span>,
|
||||
opt_ty_info: Option<Span>,
|
||||
infcx: &crate::BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
|
||||
) -> Option<AmpMutSugg> {
|
||||
// if there is a RHS and it starts with a `&` from it, then check if it is
|
||||
let tcx = infcx.tcx;
|
||||
// If there is a RHS and it starts with a `&` from it, then check if it is
|
||||
// mutable, and if not, put suggest putting `mut ` to make it mutable.
|
||||
// we don't have to worry about lifetime annotations here because they are
|
||||
// We don't have to worry about lifetime annotations here because they are
|
||||
// not valid when taking a reference. For example, the following is not valid Rust:
|
||||
//
|
||||
// let x: &i32 = &'a 5;
|
||||
// ^^ lifetime annotation not allowed
|
||||
//
|
||||
if let Some(rhs_span) = opt_assignment_rhs_span
|
||||
&& let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
&& let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
|
||||
if let Some(rhs_stmt) = opt_assignment_rhs_stmt
|
||||
&& let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
|
||||
&& let mut rhs_span = rhs_stmt.source_info.span
|
||||
&& let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
{
|
||||
// Suggest changing `&raw const` to `&raw mut` if applicable.
|
||||
if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
|
||||
let const_idx = rhs_str.find("const").unwrap() as u32;
|
||||
let const_span = rhs_span
|
||||
.with_lo(rhs_span.lo() + BytePos(const_idx))
|
||||
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
|
||||
let mut rvalue = rvalue;
|
||||
|
||||
return Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span: const_span,
|
||||
suggestion: "mut".to_owned(),
|
||||
additional: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Figure out if rhs already is `&mut`.
|
||||
let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
|
||||
match rest.chars().next() {
|
||||
// e.g. `&mut x`
|
||||
Some(c) if c.is_whitespace() => true,
|
||||
// e.g. `&mut(x)`
|
||||
Some('(') => true,
|
||||
// e.g. `&mut{x}`
|
||||
Some('{') => true,
|
||||
// e.g. `&mutablevar`
|
||||
_ => false,
|
||||
// Take some special care when handling `let _x = &*_y`:
|
||||
// We want to know if this is part of an overloaded index, so `let x = &a[0]`,
|
||||
// or whether this is a usertype ascription (`let _x: &T = y`).
|
||||
if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
|
||||
&& place.projection.len() == 1
|
||||
&& place.projection[0] == ProjectionElem::Deref
|
||||
&& let Some(assign) = find_assignments(&body, place.local).first()
|
||||
{
|
||||
// If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
|
||||
// to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
|
||||
// suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
|
||||
if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
|
||||
&& let [user_ty_proj] = user_ty_projs.contents.as_slice()
|
||||
&& user_ty_proj.projs.is_empty()
|
||||
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
|
||||
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
|
||||
&& let rhs_span_new = rhs_stmt_new.source_info.span
|
||||
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
{
|
||||
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// if the reference is already mutable then there is nothing we can do
|
||||
// here.
|
||||
if !is_mut {
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
|
||||
|
||||
// FIXME(Ezrashaw): returning is bad because we still might want to
|
||||
// update the annotated type, see #106857.
|
||||
return Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: "mut ".to_owned(),
|
||||
additional: None,
|
||||
});
|
||||
if let Either::Right(call) = body.stmt_at(*assign)
|
||||
&& let TerminatorKind::Call {
|
||||
func: Operand::Constant(box const_operand), args, ..
|
||||
} = &call.kind
|
||||
&& let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
|
||||
&& let Some(trait_) = tcx.trait_of_assoc(method_def_id)
|
||||
&& tcx.is_lang_item(trait_, hir::LangItem::Index)
|
||||
{
|
||||
let trait_ref = ty::TraitRef::from_assoc(
|
||||
tcx,
|
||||
tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
|
||||
method_args,
|
||||
);
|
||||
// The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
|
||||
if !infcx
|
||||
.type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
|
||||
.must_apply_considering_regions()
|
||||
{
|
||||
// Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
|
||||
if let ty::Adt(def, _) = trait_ref.self_ty().kind()
|
||||
&& [sym::BTreeMap, sym::HashMap]
|
||||
.into_iter()
|
||||
.any(|s| tcx.is_diagnostic_item(s, def.did()))
|
||||
&& let [map, key] = &**args
|
||||
&& let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
|
||||
&& let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
|
||||
{
|
||||
let span = rhs_span;
|
||||
let suggestion = format!("{map}.get_mut({key}).unwrap()");
|
||||
return Some(AmpMutSugg::MapGetMut { span, suggestion });
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sugg = match rvalue {
|
||||
Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
|
||||
// Shrink the span to just after the `&` in `&variable`.
|
||||
Some((
|
||||
rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
|
||||
"mut ".to_owned(),
|
||||
))
|
||||
}
|
||||
Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
|
||||
// Suggest changing `&raw const` to `&raw mut` if applicable.
|
||||
let const_idx = const_idx as u32;
|
||||
Some((
|
||||
rhs_span
|
||||
.with_lo(rhs_span.lo() + BytePos(const_idx))
|
||||
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
|
||||
"mut".to_owned(),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((span, suggestion)) = sugg {
|
||||
return Some(AmpMutSugg::Expr { span, suggestion });
|
||||
}
|
||||
}
|
||||
|
||||
let (binding_exists, span) = match opt_ty_info {
|
||||
// if this is a variable binding with an explicit type,
|
||||
// then we will suggest changing it to be mutable.
|
||||
// this is `Applicability::MachineApplicable`.
|
||||
Some(ty_span) => (true, ty_span),
|
||||
|
||||
// otherwise, we'll suggest *adding* an annotated type, we'll suggest
|
||||
// the RHS's type for that.
|
||||
// this is `Applicability::HasPlaceholders`.
|
||||
None => (false, decl_span),
|
||||
};
|
||||
|
||||
// if the binding already exists and is a reference with an explicit
|
||||
// lifetime, then we can suggest adding ` mut`. this is special-cased from
|
||||
// the path without an explicit lifetime.
|
||||
if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
|
||||
&& src.starts_with("&'")
|
||||
// note that `& 'a T` is invalid so this is correct.
|
||||
&& let Some(ws_pos) = src.find(char::is_whitespace)
|
||||
{
|
||||
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
|
||||
// if there is already a binding, we modify it to be `mut`
|
||||
} else if binding_exists {
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
|
||||
} else {
|
||||
// otherwise, suggest that the user annotates the binding; we provide the
|
||||
// type of the local.
|
||||
let ty = decl_ty.builtin_deref(true).unwrap();
|
||||
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: false,
|
||||
span,
|
||||
suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
|
||||
additional: None,
|
||||
})
|
||||
}
|
||||
Some(AmpMutSugg::ChangeBinding)
|
||||
}
|
||||
|
||||
/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ use rustc_middle::mir::{self, ConstraintCategory, Location};
|
|||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
|
||||
use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
|
||||
|
||||
use crate::MirBorrowckCtxt;
|
||||
|
|
@ -26,13 +28,61 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if errors.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let infcx = self.infcx;
|
||||
let mut guar = None;
|
||||
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
|
||||
None;
|
||||
for error in errors {
|
||||
guar = Some(match error {
|
||||
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx),
|
||||
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
|
||||
DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
|
||||
self.infcx.dcx().emit_err(err)
|
||||
infcx.dcx().emit_err(err)
|
||||
}
|
||||
DeferredOpaqueTypeError::UnexpectedHiddenRegion {
|
||||
opaque_type_key,
|
||||
hidden_type,
|
||||
member_region,
|
||||
} => {
|
||||
let named_ty =
|
||||
self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty);
|
||||
let named_key = self
|
||||
.regioncx
|
||||
.name_regions_for_member_constraint(infcx.tcx, opaque_type_key);
|
||||
let named_region =
|
||||
self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region);
|
||||
let diag = unexpected_hidden_region_diagnostic(
|
||||
infcx,
|
||||
self.mir_def_id(),
|
||||
hidden_type.span,
|
||||
named_ty,
|
||||
named_region,
|
||||
named_key,
|
||||
);
|
||||
if last_unexpected_hidden_region
|
||||
!= Some((hidden_type.span, named_ty, named_key))
|
||||
{
|
||||
last_unexpected_hidden_region =
|
||||
Some((hidden_type.span, named_ty, named_key));
|
||||
diag.emit()
|
||||
} else {
|
||||
diag.delay_as_bug()
|
||||
}
|
||||
}
|
||||
DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
|
||||
span,
|
||||
opaque_type_key,
|
||||
} => infcx.dcx().span_err(
|
||||
span,
|
||||
format!(
|
||||
"non-defining use of `{}` in the defining scope",
|
||||
Ty::new_opaque(
|
||||
infcx.tcx,
|
||||
opaque_type_key.def_id.to_def_id(),
|
||||
opaque_type_key.args
|
||||
)
|
||||
),
|
||||
),
|
||||
});
|
||||
}
|
||||
let guar = guar.unwrap();
|
||||
|
|
@ -193,10 +243,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
|
|||
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
|
||||
|
||||
// Find a path between the borrow region and our opaque capture.
|
||||
if let Some((path, _)) =
|
||||
self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
|
||||
r == opaque_region_vid
|
||||
})
|
||||
if let Some(path) = self
|
||||
.regioncx
|
||||
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
|
||||
{
|
||||
for constraint in path {
|
||||
// If we find a call in this path, then check if it defines the opaque.
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{
|
|||
self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type,
|
||||
find_param_with_region, suggest_adding_lifetime_params,
|
||||
};
|
||||
use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
|
@ -62,7 +61,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
|||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal
|
||||
| ConstraintCategory::IllegalUniverse => "",
|
||||
| ConstraintCategory::OutlivesUnnameablePlaceholder(..) => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> {
|
|||
/// A generic bound failure for a type test (`T: 'a`).
|
||||
TypeTestError { type_test: TypeTest<'tcx> },
|
||||
|
||||
/// An unexpected hidden region for an opaque type.
|
||||
UnexpectedHiddenRegion {
|
||||
/// The span for the member constraint.
|
||||
span: Span,
|
||||
/// The hidden type.
|
||||
hidden_ty: Ty<'tcx>,
|
||||
/// The opaque type.
|
||||
key: ty::OpaqueTypeKey<'tcx>,
|
||||
/// The unexpected region.
|
||||
member_region: ty::Region<'tcx>,
|
||||
},
|
||||
|
||||
/// Higher-ranked subtyping error.
|
||||
BoundUniversalRegionError {
|
||||
/// The placeholder free region.
|
||||
|
|
@ -215,7 +202,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
diag: &mut Diag<'_>,
|
||||
lower_bound: RegionVid,
|
||||
) {
|
||||
let mut suggestions = vec![];
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
// find generic associated types in the given region 'lower_bound'
|
||||
|
|
@ -237,9 +223,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.collect::<Vec<_>>();
|
||||
debug!(?gat_id_and_generics);
|
||||
|
||||
// find higher-ranked trait bounds bounded to the generic associated types
|
||||
// Look for the where-bound which introduces the placeholder.
|
||||
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
|
||||
// and `T: for<'a> Trait`<'a>.
|
||||
let mut hrtb_bounds = vec![];
|
||||
gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
|
||||
gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
|
||||
for pred in generics.predicates {
|
||||
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
|
||||
pred.kind
|
||||
|
|
@ -248,17 +236,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
};
|
||||
if bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
for bound in *bounds {
|
||||
hrtb_bounds.push(bound);
|
||||
}
|
||||
} else {
|
||||
for bound in *bounds {
|
||||
if let Trait(trait_bound) = bound {
|
||||
if trait_bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
hrtb_bounds.push(bound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
debug!(?hrtb_bounds);
|
||||
|
||||
let mut suggestions = vec![];
|
||||
hrtb_bounds.iter().for_each(|bound| {
|
||||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
|
||||
return;
|
||||
|
|
@ -307,11 +310,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
|
||||
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
|
||||
// buffered in the `MirBorrowckCtxt`.
|
||||
|
||||
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
|
||||
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
|
||||
None;
|
||||
|
||||
for (nll_error, _) in nll_errors.into_iter() {
|
||||
match nll_error {
|
||||
RegionErrorKind::TypeTestError { type_test } => {
|
||||
|
|
@ -362,30 +361,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
|
||||
let named_ty =
|
||||
self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
|
||||
let named_key =
|
||||
self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key);
|
||||
let named_region = self
|
||||
.regioncx
|
||||
.name_regions_for_member_constraint(self.infcx.tcx, member_region);
|
||||
let diag = unexpected_hidden_region_diagnostic(
|
||||
self.infcx,
|
||||
self.mir_def_id(),
|
||||
span,
|
||||
named_ty,
|
||||
named_region,
|
||||
named_key,
|
||||
);
|
||||
if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
|
||||
self.buffer_error(diag);
|
||||
last_unexpected_hidden_region = Some((span, named_ty, named_key));
|
||||
} else {
|
||||
diag.delay_as_bug();
|
||||
}
|
||||
}
|
||||
|
||||
RegionErrorKind::BoundUniversalRegionError {
|
||||
longer_fr,
|
||||
placeholder,
|
||||
|
|
@ -394,11 +369,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let (_, cause) = self.regioncx.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::Placeholder(placeholder),
|
||||
error_vid,
|
||||
);
|
||||
let cause = self
|
||||
.regioncx
|
||||
.best_blame_constraint(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::Placeholder(placeholder),
|
||||
error_vid,
|
||||
)
|
||||
.0
|
||||
.cause;
|
||||
|
||||
let universe = placeholder.universe;
|
||||
let universe_info = self.regioncx.universe_info(universe);
|
||||
|
|
@ -454,9 +433,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
) {
|
||||
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
let (blame_constraint, path) =
|
||||
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
|
||||
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
|
||||
|
||||
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//! Logic for lowering higher-kinded outlives constraints
|
||||
//! (with placeholders and universes) and turn them into regular
|
||||
//! outlives constraints.
|
||||
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph::scc;
|
||||
|
|
@ -10,12 +9,11 @@ use rustc_index::IndexVec;
|
|||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, UniverseIndex};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
|
||||
use crate::consumers::OutlivesConstraint;
|
||||
use crate::diagnostics::UniverseInfo;
|
||||
use crate::member_constraints::MemberConstraintSet;
|
||||
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
|
||||
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
|
||||
use crate::ty::VarianceDiagInfo;
|
||||
|
|
@ -30,7 +28,6 @@ pub(crate) struct LoweredConstraints<'tcx> {
|
|||
pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
|
||||
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
|
||||
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
|
||||
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
|
||||
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
|
||||
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
|
||||
pub(crate) liveness_constraints: LivenessValues,
|
||||
|
|
@ -64,18 +61,71 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
|||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
enum PlaceholderReachability {
|
||||
/// This SCC reaches no placeholders.
|
||||
NoPlaceholders,
|
||||
/// This SCC reaches at least one placeholder.
|
||||
Placeholders {
|
||||
/// The largest-universed placeholder we can reach
|
||||
max_universe: (UniverseIndex, RegionVid),
|
||||
|
||||
/// The placeholder with the smallest ID
|
||||
min_placeholder: RegionVid,
|
||||
|
||||
/// The placeholder with the largest ID
|
||||
max_placeholder: RegionVid,
|
||||
},
|
||||
}
|
||||
|
||||
impl PlaceholderReachability {
|
||||
/// Merge the reachable placeholders of two graph components.
|
||||
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
|
||||
use PlaceholderReachability::*;
|
||||
match (self, other) {
|
||||
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
|
||||
(NoPlaceholders, p @ Placeholders { .. })
|
||||
| (p @ Placeholders { .. }, NoPlaceholders) => p,
|
||||
(
|
||||
Placeholders {
|
||||
min_placeholder: min_pl,
|
||||
max_placeholder: max_pl,
|
||||
max_universe: max_u,
|
||||
},
|
||||
Placeholders { min_placeholder, max_placeholder, max_universe },
|
||||
) => Placeholders {
|
||||
min_placeholder: min_pl.min(min_placeholder),
|
||||
max_placeholder: max_pl.max(max_placeholder),
|
||||
max_universe: max_u.max(max_universe),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
|
||||
match self {
|
||||
Self::NoPlaceholders => None,
|
||||
Self::Placeholders { max_universe, .. } => Some(*max_universe),
|
||||
}
|
||||
}
|
||||
|
||||
/// If we have reached placeholders, determine if they can
|
||||
/// be named from this universe.
|
||||
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
|
||||
self.max_universe()
|
||||
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
|
||||
}
|
||||
}
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
reachable_placeholders: PlaceholderReachability,
|
||||
|
||||
/// The largest universe nameable from this SCC.
|
||||
/// It is the smallest nameable universes of all
|
||||
/// existential regions reachable from it.
|
||||
max_nameable_universe: UniverseIndex,
|
||||
/// existential regions reachable from it. Small Rvids are preferred.
|
||||
max_nameable_universe: (UniverseIndex, RegionVid),
|
||||
|
||||
/// The representative Region Variable Id for this SCC.
|
||||
pub(crate) representative: Representative,
|
||||
|
|
@ -83,69 +133,78 @@ pub(crate) struct RegionTracker {
|
|||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let placeholder_universe =
|
||||
let reachable_placeholders =
|
||||
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
|
||||
definition.universe
|
||||
PlaceholderReachability::Placeholders {
|
||||
max_universe: (definition.universe, rvid),
|
||||
min_placeholder: rvid,
|
||||
max_placeholder: rvid,
|
||||
}
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
PlaceholderReachability::NoPlaceholders
|
||||
};
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
max_nameable_universe: definition.universe,
|
||||
reachable_placeholders,
|
||||
max_nameable_universe: (definition.universe, rvid),
|
||||
representative: Representative::new(rvid, definition),
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest universe this SCC can name. It's the smallest
|
||||
/// largest nameable uninverse of any reachable region.
|
||||
/// largest nameable universe of any reachable region, or
|
||||
/// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
|
||||
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
|
||||
self.max_nameable_universe
|
||||
self.max_nameable_universe.0
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.max_nameable_universe =
|
||||
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
|
||||
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
|
||||
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
|
||||
universe
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest nameable universe of any
|
||||
/// reachable existential region.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
/// Determine if we can name all the placeholders in `other`.
|
||||
pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool {
|
||||
other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0)
|
||||
}
|
||||
|
||||
/// Determine if the tracked universes of the two SCCs are compatible.
|
||||
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
|
||||
self.max_nameable_universe().can_name(other.max_nameable_universe())
|
||||
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
|
||||
/// If this SCC reaches a placeholder it can't name, return it.
|
||||
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
|
||||
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
|
||||
!self.max_nameable_universe().can_name(placeholder_universe)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, other: Self) -> Self {
|
||||
self.representative = self.representative.merge_scc(other.representative);
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
trace!("{:?} << {:?}", self.representative, other.representative);
|
||||
|
||||
Self {
|
||||
representative: self.representative.min(other.representative),
|
||||
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
|
||||
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
fn merge_reached(self, other: Self) -> Self {
|
||||
Self {
|
||||
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
|
||||
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
|
||||
representative: self.representative,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the region variable definitions contain
|
||||
/// placeholders, and compute them for later use.
|
||||
fn region_definitions<'tcx>(
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
// FIXME: This is also used by opaque type handling. Move it to a separate file.
|
||||
pub(super) fn region_definitions<'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
|
||||
let var_infos = infcx.get_region_var_infos();
|
||||
// Create a RegionDefinition for each inference variable. This happens here because
|
||||
|
|
@ -157,7 +216,7 @@ fn region_definitions<'tcx>(
|
|||
for info in var_infos.iter() {
|
||||
let origin = match info.origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
_ => NllRegionVariableOrigin::Existential { from_forall: false, name: None },
|
||||
_ => NllRegionVariableOrigin::Existential { name: None },
|
||||
};
|
||||
|
||||
let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
|
||||
|
|
@ -209,14 +268,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
|||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
) -> LoweredConstraints<'tcx> {
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
|
||||
let (definitions, has_placeholders) = region_definitions(infcx, universal_regions);
|
||||
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
liveness_constraints,
|
||||
mut outlives_constraints,
|
||||
member_constraints,
|
||||
universe_causes,
|
||||
type_tests,
|
||||
} = constraints;
|
||||
|
|
@ -242,7 +300,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
|||
|
||||
return LoweredConstraints {
|
||||
type_tests,
|
||||
member_constraints,
|
||||
constraint_sccs,
|
||||
scc_annotations: scc_annotations.scc_to_annotation,
|
||||
definitions,
|
||||
|
|
@ -279,7 +336,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
|||
constraint_sccs,
|
||||
definitions,
|
||||
scc_annotations,
|
||||
member_constraints,
|
||||
outlives_constraints: Frozen::freeze(outlives_constraints),
|
||||
type_tests,
|
||||
liveness_constraints,
|
||||
|
|
@ -310,28 +366,52 @@ fn rewrite_placeholder_outlives<'tcx>(
|
|||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
outlives_constraints.push(scc_representative_outlives_static);
|
||||
added_constraints = true;
|
||||
debug!("Added {:?}: 'static!", annotation.representative.rvid());
|
||||
}
|
||||
let Some((max_u, max_u_rvid)) = annotation.unnameable_placeholder() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}",
|
||||
annotation.representative
|
||||
);
|
||||
|
||||
// We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
|
||||
// That constraint is annotated with some placeholder `unnameable` where
|
||||
// `unnameable` is unnameable from `r` and there is a path in the constraint graph
|
||||
// between them.
|
||||
//
|
||||
// There is one exception; if some other region in this SCC can't name `'r`, then
|
||||
// we pick the region with the smallest universe in the SCC, so that a path can
|
||||
// always start in `'r` to find a motivation that isn't cyclic.
|
||||
let blame_to = if annotation.representative.rvid() == max_u_rvid {
|
||||
// Assertion: the region that lowered our universe is an existential one and we are a placeholder!
|
||||
|
||||
// The SCC's representative is not nameable from some region
|
||||
// that ends up in the SCC.
|
||||
let small_universed_rvid = annotation.max_nameable_universe.1;
|
||||
debug!(
|
||||
"{small_universed_rvid:?} lowered our universe to {:?}",
|
||||
annotation.max_nameable_universe()
|
||||
);
|
||||
small_universed_rvid
|
||||
} else {
|
||||
// `max_u_rvid` is not nameable by the SCC's representative.
|
||||
max_u_rvid
|
||||
};
|
||||
|
||||
// FIXME: if we can extract a useful blame span here, future error
|
||||
// reporting and constraint search can be simplified.
|
||||
|
||||
added_constraints = true;
|
||||
outlives_constraints.push(OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::OutlivesUnnameablePlaceholder(blame_to),
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
});
|
||||
}
|
||||
added_constraints
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ use std::ops::{ControlFlow, Deref};
|
|||
use std::rc::Rc;
|
||||
|
||||
use borrow_set::LocalsStateAtExit;
|
||||
use polonius_engine::AllFacts;
|
||||
use root_cx::BorrowCheckRootCtxt;
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_errors::LintDiagnostic;
|
||||
|
|
@ -32,6 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
|
|||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::MixedBitSet;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::outlives::env::RegionBoundPairs;
|
||||
use rustc_infer::infer::{
|
||||
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
|
||||
};
|
||||
|
|
@ -53,7 +56,7 @@ use smallvec::SmallVec;
|
|||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::borrow_set::{BorrowData, BorrowSet};
|
||||
use crate::consumers::BodyWithBorrowckFacts;
|
||||
use crate::consumers::{BodyWithBorrowckFacts, RustcFacts};
|
||||
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
|
||||
use crate::diagnostics::{
|
||||
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
|
||||
|
|
@ -61,15 +64,17 @@ use crate::diagnostics::{
|
|||
use crate::path_utils::*;
|
||||
use crate::place_ext::PlaceExt;
|
||||
use crate::places_conflict::{PlaceConflictBias, places_conflict};
|
||||
use crate::polonius::PoloniusDiagnosticsContext;
|
||||
use crate::polonius::legacy::{
|
||||
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
|
||||
};
|
||||
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
|
||||
use crate::prefixes::PrefixSet;
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
|
||||
use crate::renumber::RegionCtxt;
|
||||
use crate::session_diagnostics::VarNeedNotMut;
|
||||
use crate::type_check::MirTypeckResults;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults};
|
||||
|
||||
mod borrow_set;
|
||||
mod borrowck_errors;
|
||||
|
|
@ -78,7 +83,6 @@ mod dataflow;
|
|||
mod def_use;
|
||||
mod diagnostics;
|
||||
mod handle_placeholders;
|
||||
mod member_constraints;
|
||||
mod nll;
|
||||
mod path_utils;
|
||||
mod place_ext;
|
||||
|
|
@ -130,18 +134,7 @@ fn mir_borrowck(
|
|||
Ok(tcx.arena.alloc(opaque_types))
|
||||
} else {
|
||||
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
|
||||
// We need to manually borrowck all nested bodies from the HIR as
|
||||
// we do not generate MIR for dead code. Not doing so causes us to
|
||||
// never check closures in dead code.
|
||||
let nested_bodies = tcx.nested_bodies_within(def);
|
||||
for def_id in nested_bodies {
|
||||
root_cx.get_or_insert_nested(def_id);
|
||||
}
|
||||
|
||||
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
|
||||
do_mir_borrowck(&mut root_cx, def);
|
||||
debug_assert!(closure_requirements.is_none());
|
||||
debug_assert!(used_mut_upvars.is_empty());
|
||||
root_cx.do_mir_borrowck();
|
||||
root_cx.finalize()
|
||||
}
|
||||
}
|
||||
|
|
@ -154,6 +147,8 @@ struct PropagatedBorrowCheckResults<'tcx> {
|
|||
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
|
||||
}
|
||||
|
||||
type DeferredClosureRequirements<'tcx> = Vec<(LocalDefId, ty::GenericArgsRef<'tcx>, Locations)>;
|
||||
|
||||
/// After we borrow check a closure, we are left with various
|
||||
/// requirements that we have inferred between the free regions that
|
||||
/// appear in the closure's signature or on its field types. These
|
||||
|
|
@ -292,16 +287,33 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Perform the actual borrow checking.
|
||||
///
|
||||
/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
|
||||
#[instrument(skip(root_cx), level = "debug")]
|
||||
fn do_mir_borrowck<'tcx>(
|
||||
struct CollectRegionConstraintsResult<'tcx> {
|
||||
infcx: BorrowckInferCtxt<'tcx>,
|
||||
body_owned: Body<'tcx>,
|
||||
promoted: IndexVec<Promoted, Body<'tcx>>,
|
||||
move_data: MoveData<'tcx>,
|
||||
borrow_set: BorrowSet<'tcx>,
|
||||
location_table: PoloniusLocationTable,
|
||||
location_map: Rc<DenseLocationMap>,
|
||||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
|
||||
known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
|
||||
deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
|
||||
polonius_facts: Option<AllFacts<RustcFacts>>,
|
||||
polonius_context: Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
/// Start borrow checking by collecting the region constraints for
|
||||
/// the current body. This initializes the relevant data structures
|
||||
/// and then type checks the MIR body.
|
||||
fn borrowck_collect_region_constraints<'tcx>(
|
||||
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
|
||||
def: LocalDefId,
|
||||
) -> PropagatedBorrowCheckResults<'tcx> {
|
||||
) -> CollectRegionConstraintsResult<'tcx> {
|
||||
let tcx = root_cx.tcx;
|
||||
let infcx = BorrowckInferCtxt::new(tcx, def);
|
||||
let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id());
|
||||
let (input_body, promoted) = tcx.mir_promoted(def);
|
||||
let input_body: &Body<'_> = &input_body.borrow();
|
||||
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
|
||||
|
|
@ -337,7 +349,9 @@ fn do_mir_borrowck<'tcx>(
|
|||
let MirTypeckResults {
|
||||
constraints,
|
||||
universal_region_relations,
|
||||
opaque_type_values,
|
||||
region_bound_pairs,
|
||||
known_type_outlives_obligations,
|
||||
deferred_closure_requirements,
|
||||
polonius_context,
|
||||
} = type_check::type_check(
|
||||
root_cx,
|
||||
|
|
@ -352,6 +366,54 @@ fn do_mir_borrowck<'tcx>(
|
|||
Rc::clone(&location_map),
|
||||
);
|
||||
|
||||
CollectRegionConstraintsResult {
|
||||
infcx,
|
||||
body_owned,
|
||||
promoted,
|
||||
move_data,
|
||||
borrow_set,
|
||||
location_table,
|
||||
location_map,
|
||||
universal_region_relations,
|
||||
region_bound_pairs,
|
||||
known_type_outlives_obligations,
|
||||
constraints,
|
||||
deferred_closure_requirements,
|
||||
deferred_opaque_type_errors: Default::default(),
|
||||
polonius_facts,
|
||||
polonius_context,
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the region constraints computed by [borrowck_collect_region_constraints]
|
||||
/// and the additional constraints from [BorrowCheckRootCtxt::handle_opaque_type_uses],
|
||||
/// compute the region graph and actually check for any borrowck errors.
|
||||
fn borrowck_check_region_constraints<'tcx>(
|
||||
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
|
||||
CollectRegionConstraintsResult {
|
||||
infcx,
|
||||
body_owned,
|
||||
promoted,
|
||||
move_data,
|
||||
borrow_set,
|
||||
location_table,
|
||||
location_map,
|
||||
universal_region_relations,
|
||||
region_bound_pairs: _,
|
||||
known_type_outlives_obligations: _,
|
||||
constraints,
|
||||
deferred_closure_requirements,
|
||||
deferred_opaque_type_errors,
|
||||
polonius_facts,
|
||||
polonius_context,
|
||||
}: CollectRegionConstraintsResult<'tcx>,
|
||||
) -> PropagatedBorrowCheckResults<'tcx> {
|
||||
assert!(!infcx.has_opaque_types_in_storage());
|
||||
assert!(deferred_closure_requirements.is_empty());
|
||||
let tcx = root_cx.tcx;
|
||||
let body = &body_owned;
|
||||
let def = body.source.def_id().expect_local();
|
||||
|
||||
// Compute non-lexical lifetimes using the constraints computed
|
||||
// by typechecking the MIR body.
|
||||
let nll::NllOutput {
|
||||
|
|
@ -375,8 +437,6 @@ fn do_mir_borrowck<'tcx>(
|
|||
polonius_context,
|
||||
);
|
||||
|
||||
let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This lets us
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set);
|
||||
|
|
@ -472,7 +532,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
|
||||
// Compute and report region errors, if any.
|
||||
if nll_errors.is_empty() {
|
||||
mbcx.report_opaque_type_errors(opaque_type_errors);
|
||||
mbcx.report_opaque_type_errors(deferred_opaque_type_errors);
|
||||
} else {
|
||||
mbcx.report_region_errors(nll_errors);
|
||||
}
|
||||
|
|
@ -581,12 +641,13 @@ fn get_flow_results<'a, 'tcx>(
|
|||
|
||||
pub(crate) struct BorrowckInferCtxt<'tcx> {
|
||||
pub(crate) infcx: InferCtxt<'tcx>,
|
||||
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
|
||||
pub(crate) root_def_id: LocalDefId,
|
||||
pub(crate) param_env: ParamEnv<'tcx>,
|
||||
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
|
||||
}
|
||||
|
||||
impl<'tcx> BorrowckInferCtxt<'tcx> {
|
||||
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
|
||||
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId) -> Self {
|
||||
let typing_mode = if tcx.use_typing_mode_borrowck() {
|
||||
TypingMode::borrowck(tcx, def_id)
|
||||
} else {
|
||||
|
|
@ -594,7 +655,12 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
|
|||
};
|
||||
let infcx = tcx.infer_ctxt().build(typing_mode);
|
||||
let param_env = tcx.param_env(def_id);
|
||||
BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
|
||||
BorrowckInferCtxt {
|
||||
infcx,
|
||||
root_def_id,
|
||||
reg_var_to_origin: RefCell::new(Default::default()),
|
||||
param_env,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next_region_var<F>(
|
||||
|
|
@ -1491,9 +1557,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
&Rvalue::Discriminant(place) => {
|
||||
let af = match *rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
@ -1838,7 +1903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
| ty::Slice(_)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Dynamic(_, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::CoroutineClosure(_, _)
|
||||
| ty::Coroutine(_, _)
|
||||
|
|
@ -1884,7 +1949,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
| ty::Ref(_, _, _)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Dynamic(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::UnsafeBinder(_)
|
||||
|
|
|
|||
|
|
@ -1,226 +0,0 @@
|
|||
use std::hash::Hash;
|
||||
use std::ops::Index;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
use tracing::instrument;
|
||||
|
||||
/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
|
||||
/// indexed by the region `R0`.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MemberConstraintSet<'tcx, R>
|
||||
where
|
||||
R: Copy + Eq,
|
||||
{
|
||||
/// Stores the first "member" constraint for a given `R0`. This is an
|
||||
/// index into the `constraints` vector below.
|
||||
first_constraints: FxIndexMap<R, NllMemberConstraintIndex>,
|
||||
|
||||
/// Stores the data about each `R0 member of [R1..Rn]` constraint.
|
||||
/// These are organized into a linked list, so each constraint
|
||||
/// contains the index of the next constraint with the same `R0`.
|
||||
constraints: IndexVec<NllMemberConstraintIndex, MemberConstraint<'tcx>>,
|
||||
|
||||
/// Stores the `R1..Rn` regions for *all* sets. For any given
|
||||
/// constraint, we keep two indices so that we can pull out a
|
||||
/// slice.
|
||||
choice_regions: Vec<ty::RegionVid>,
|
||||
}
|
||||
|
||||
/// Represents a `R0 member of [R1..Rn]` constraint
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MemberConstraint<'tcx> {
|
||||
next_constraint: Option<NllMemberConstraintIndex>,
|
||||
|
||||
/// The span where the hidden type was instantiated.
|
||||
pub(crate) definition_span: Span,
|
||||
|
||||
/// The hidden type in which `R0` appears. (Used in error reporting.)
|
||||
pub(crate) hidden_ty: Ty<'tcx>,
|
||||
|
||||
pub(crate) key: ty::OpaqueTypeKey<'tcx>,
|
||||
|
||||
/// The region `R0`.
|
||||
pub(crate) member_region_vid: ty::RegionVid,
|
||||
|
||||
/// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
|
||||
start_index: usize,
|
||||
|
||||
/// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
|
||||
end_index: usize,
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[debug_format = "MemberConstraintIndex({})"]
|
||||
pub(crate) struct NllMemberConstraintIndex {}
|
||||
}
|
||||
|
||||
impl Default for MemberConstraintSet<'_, ty::RegionVid> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first_constraints: Default::default(),
|
||||
constraints: Default::default(),
|
||||
choice_regions: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.constraints.is_empty()
|
||||
}
|
||||
|
||||
/// Pushes a member constraint into the set.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn add_member_constraint(
|
||||
&mut self,
|
||||
key: ty::OpaqueTypeKey<'tcx>,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
definition_span: Span,
|
||||
member_region_vid: ty::RegionVid,
|
||||
choice_regions: &[ty::RegionVid],
|
||||
) {
|
||||
let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
|
||||
let start_index = self.choice_regions.len();
|
||||
self.choice_regions.extend(choice_regions);
|
||||
let end_index = self.choice_regions.len();
|
||||
let constraint_index = self.constraints.push(MemberConstraint {
|
||||
next_constraint,
|
||||
member_region_vid,
|
||||
definition_span,
|
||||
hidden_ty,
|
||||
key,
|
||||
start_index,
|
||||
end_index,
|
||||
});
|
||||
self.first_constraints.insert(member_region_vid, constraint_index);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, R1> MemberConstraintSet<'tcx, R1>
|
||||
where
|
||||
R1: Copy + Hash + Eq,
|
||||
{
|
||||
/// Remap the "member region" key using `map_fn`, producing a new
|
||||
/// member constraint set. This is used in the NLL code to map from
|
||||
/// the original `RegionVid` to an scc index. In some cases, we
|
||||
/// may have multiple `R1` values mapping to the same `R2` key -- that
|
||||
/// is ok, the two sets will be merged.
|
||||
pub(crate) fn into_mapped<R2>(
|
||||
self,
|
||||
mut map_fn: impl FnMut(R1) -> R2,
|
||||
) -> MemberConstraintSet<'tcx, R2>
|
||||
where
|
||||
R2: Copy + Hash + Eq,
|
||||
{
|
||||
// We can re-use most of the original data, just tweaking the
|
||||
// linked list links a bit.
|
||||
//
|
||||
// For example if we had two keys `Ra` and `Rb` that both now
|
||||
// wind up mapped to the same key `S`, we would append the
|
||||
// linked list for `Ra` onto the end of the linked list for
|
||||
// `Rb` (or vice versa) -- this basically just requires
|
||||
// rewriting the final link from one list to point at the other
|
||||
// other (see `append_list`).
|
||||
|
||||
let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
|
||||
|
||||
let mut first_constraints2 = FxIndexMap::default();
|
||||
first_constraints2.reserve(first_constraints.len());
|
||||
|
||||
for (r1, start1) in first_constraints {
|
||||
let r2 = map_fn(r1);
|
||||
if let Some(&start2) = first_constraints2.get(&r2) {
|
||||
append_list(&mut constraints, start1, start2);
|
||||
}
|
||||
first_constraints2.insert(r2, start1);
|
||||
}
|
||||
|
||||
MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, R> MemberConstraintSet<'tcx, R>
|
||||
where
|
||||
R: Copy + Hash + Eq,
|
||||
{
|
||||
pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> {
|
||||
self.constraints.indices()
|
||||
}
|
||||
|
||||
/// Iterate down the constraint indices associated with a given
|
||||
/// peek-region. You can then use `choice_regions` and other
|
||||
/// methods to access data.
|
||||
pub(crate) fn indices(
|
||||
&self,
|
||||
member_region_vid: R,
|
||||
) -> impl Iterator<Item = NllMemberConstraintIndex> {
|
||||
let mut next = self.first_constraints.get(&member_region_vid).cloned();
|
||||
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
|
||||
if let Some(current) = next {
|
||||
next = self.constraints[current].next_constraint;
|
||||
Some(current)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the "choice regions" for a given member
|
||||
/// constraint. This is the `R1..Rn` from a constraint like:
|
||||
///
|
||||
/// ```text
|
||||
/// R0 member of [R1..Rn]
|
||||
/// ```
|
||||
pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
|
||||
let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
|
||||
&self.choice_regions[*start_index..*end_index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
|
||||
where
|
||||
R: Copy + Eq,
|
||||
{
|
||||
type Output = MemberConstraint<'tcx>;
|
||||
|
||||
fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> {
|
||||
&self.constraints[i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a linked list starting at `source_list` and another linked
|
||||
/// list starting at `target_list`, modify `target_list` so that it is
|
||||
/// followed by `source_list`.
|
||||
///
|
||||
/// Before:
|
||||
///
|
||||
/// ```text
|
||||
/// target_list: A -> B -> C -> (None)
|
||||
/// source_list: D -> E -> F -> (None)
|
||||
/// ```
|
||||
///
|
||||
/// After:
|
||||
///
|
||||
/// ```text
|
||||
/// target_list: A -> B -> C -> D -> E -> F -> (None)
|
||||
/// ```
|
||||
fn append_list(
|
||||
constraints: &mut IndexSlice<NllMemberConstraintIndex, MemberConstraint<'_>>,
|
||||
target_list: NllMemberConstraintIndex,
|
||||
source_list: NllMemberConstraintIndex,
|
||||
) {
|
||||
let mut p = target_list;
|
||||
loop {
|
||||
let r = &mut constraints[p];
|
||||
match r.next_constraint {
|
||||
Some(q) => p = q,
|
||||
None => {
|
||||
r.next_constraint = Some(source_list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ use std::str::FromStr;
|
|||
use polonius_engine::{Algorithm, AllFacts, Output};
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
|
||||
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
|
||||
use rustc_middle::mir::pretty::PrettyPrintMirOptions;
|
||||
use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
|
|
@ -68,11 +68,45 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
|
|||
// Replace all remaining regions with fresh inference variables.
|
||||
renumber::renumber_mir(infcx, body, promoted);
|
||||
|
||||
dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
universal_regions
|
||||
}
|
||||
|
||||
/// Computes the closure requirements given the current inference state.
|
||||
///
|
||||
/// This is intended to be used by before [BorrowCheckRootCtxt::handle_opaque_type_uses]
|
||||
/// because applying member constraints may rely on closure requirements.
|
||||
/// This is frequently the case of async functions where pretty much everything
|
||||
/// happens inside of the inner async block but the opaque only gets constrained
|
||||
/// in the parent function.
|
||||
pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
location_map: Rc<DenseLocationMap>,
|
||||
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) -> Option<ClosureRegionRequirements<'tcx>> {
|
||||
// FIXME(#146079): we shouldn't have to clone all this stuff here.
|
||||
// Computing the region graph should take at least some of it by reference/`Rc`.
|
||||
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
|
||||
constraints.clone(),
|
||||
&universal_region_relations,
|
||||
infcx,
|
||||
);
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
&infcx,
|
||||
lowered_constraints,
|
||||
universal_region_relations.clone(),
|
||||
location_map,
|
||||
);
|
||||
|
||||
let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None);
|
||||
closure_region_requirements
|
||||
}
|
||||
|
||||
/// Computes the (non-lexical) regions from the input MIR.
|
||||
///
|
||||
/// This may result in errors being reported.
|
||||
|
|
@ -175,9 +209,7 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
if !dump_enabled(tcx, "nll", body.source.def_id()) {
|
||||
return;
|
||||
}
|
||||
let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
|
||||
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
|
||||
// #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
|
||||
|
|
@ -188,27 +220,24 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
dump_mir_with_options(
|
||||
tcx,
|
||||
false,
|
||||
"nll",
|
||||
&0,
|
||||
body,
|
||||
|pass_where, out| {
|
||||
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
|
||||
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
|
||||
};
|
||||
|
||||
let dumper = dumper.set_extra_data(extra_data).set_options(options);
|
||||
|
||||
dumper.dump_mir(body);
|
||||
|
||||
// Also dump the region constraint graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
|
||||
let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
|
||||
regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
|
||||
};
|
||||
|
||||
// Also dump the region constraint SCC graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
|
||||
let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
|
||||
regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ use std::io;
|
|||
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::pretty::{
|
||||
PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
|
||||
};
|
||||
use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
|
@ -33,22 +31,41 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if !dump_enabled(tcx, "polonius", body.source.def_id()) {
|
||||
return;
|
||||
}
|
||||
let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
|
||||
|
||||
let polonius_diagnostics =
|
||||
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
|
||||
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
|
||||
emit_polonius_dump(
|
||||
let extra_data = &|pass_where, out: &mut dyn io::Write| {
|
||||
emit_polonius_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
};
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
|
||||
// mir-include-spans` on the CLI still has priority.
|
||||
let options = PrettyPrintMirOptions {
|
||||
include_extra_comments: matches!(
|
||||
tcx.sess.opts.unstable_opts.mir_include_spans,
|
||||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
|
||||
let dumper = dumper.set_extra_data(extra_data).set_options(options);
|
||||
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = dumper.create_dump_file("html", body)?;
|
||||
emit_polonius_dump(
|
||||
&dumper,
|
||||
body,
|
||||
regioncx,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
closure_region_requirements,
|
||||
&mut file,
|
||||
)?;
|
||||
};
|
||||
|
|
@ -61,12 +78,11 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
/// - a mermaid graph of the NLL regions and the constraints between them
|
||||
/// - a mermaid graph of the NLL SCCs and the constraints between them
|
||||
fn emit_polonius_dump<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dumper: &MirDumper<'_, '_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Prepare the HTML dump file prologue.
|
||||
|
|
@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "Raw MIR dump")?;
|
||||
writeln!(out, "<pre><code>")?;
|
||||
emit_html_mir(
|
||||
tcx,
|
||||
body,
|
||||
regioncx,
|
||||
borrow_set,
|
||||
&localized_outlives_constraints,
|
||||
closure_region_requirements,
|
||||
out,
|
||||
)?;
|
||||
emit_html_mir(dumper, body, out)?;
|
||||
writeln!(out, "</code></pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "NLL regions")?;
|
||||
writeln!(out, "<pre class='mermaid'>")?;
|
||||
emit_mermaid_nll_regions(tcx, regioncx, out)?;
|
||||
emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
|
||||
writeln!(out, "</pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "NLL SCCs")?;
|
||||
writeln!(out, "<pre class='mermaid'>")?;
|
||||
emit_mermaid_nll_sccs(tcx, regioncx, out)?;
|
||||
emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
|
||||
writeln!(out, "</pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -149,45 +157,14 @@ fn emit_polonius_dump<'tcx>(
|
|||
|
||||
/// Emits the polonius MIR, as escaped HTML.
|
||||
fn emit_html_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dumper: &MirDumper<'_, '_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Buffer the regular MIR dump to be able to escape it.
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
|
||||
// mir-include-spans` on the CLI still has priority.
|
||||
let options = PrettyPrintMirOptions {
|
||||
include_extra_comments: matches!(
|
||||
tcx.sess.opts.unstable_opts.mir_include_spans,
|
||||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
|
||||
dump_mir_to_writer(
|
||||
tcx,
|
||||
"polonius",
|
||||
&0,
|
||||
body,
|
||||
&mut buffer,
|
||||
|pass_where, out| {
|
||||
emit_polonius_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
localized_outlives_constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
},
|
||||
options,
|
||||
)?;
|
||||
dumper.dump_mir_to_writer(body, &mut buffer)?;
|
||||
|
||||
// Escape the handful of characters that need it. We don't need to be particularly efficient:
|
||||
// we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
|
||||
|
|
|
|||
|
|
@ -184,22 +184,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<A, B, C, D> FactRow for (A, B, C, D)
|
||||
where
|
||||
A: FactCell,
|
||||
B: FactCell,
|
||||
C: FactCell,
|
||||
D: FactCell,
|
||||
{
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut dyn Write,
|
||||
location_table: &PoloniusLocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3])
|
||||
}
|
||||
}
|
||||
|
||||
fn write_row(
|
||||
out: &mut dyn Write,
|
||||
location_table: &PoloniusLocationTable,
|
||||
|
|
|
|||
|
|
@ -306,16 +306,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
self.consume_operand(location, op);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
let af = match rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
&Rvalue::Discriminant(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Shallow(af), Read(ReadKind::Copy)),
|
||||
(Shallow(None), Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ use rustc_middle::ty::UniverseIndex;
|
|||
use super::*;
|
||||
|
||||
fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
|
||||
match constraint.locations {
|
||||
Locations::All(_) => "All(...)".to_string(),
|
||||
Locations::Single(loc) => format!("{loc:?}"),
|
||||
if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = constraint.category {
|
||||
format!("{unnameable:?} unnameable")
|
||||
} else {
|
||||
match constraint.locations {
|
||||
Locations::All(_) => "All(...)".to_string(),
|
||||
Locations::Single(loc) => format!("{loc:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::cell::OnceCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc_data_structures::binary_search_util;
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::scc::{self, Sccs};
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_index::IndexVec;
|
||||
|
|
@ -24,15 +22,13 @@ use rustc_span::hygiene::DesugaringKind;
|
|||
use rustc_span::{DUMMY_SP, Span};
|
||||
use tracing::{Level, debug, enabled, instrument, trace};
|
||||
|
||||
use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
|
||||
use crate::constraints::graph::NormalConstraintGraph;
|
||||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
||||
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
|
||||
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
|
||||
use crate::polonius::LiveLoans;
|
||||
use crate::polonius::legacy::PoloniusOutput;
|
||||
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
||||
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
|
||||
use crate::type_check::Locations;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
|
|
@ -78,18 +74,6 @@ impl Representative {
|
|||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for Representative {
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
// Just pick the smallest one. Note that we order by tag first!
|
||||
std::cmp::min(self, other)
|
||||
}
|
||||
|
||||
// For reachability, we do nothing since the representative doesn't change.
|
||||
fn merge_reached(self, _other: Self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
|
||||
|
||||
pub struct RegionInferenceContext<'tcx> {
|
||||
|
|
@ -120,20 +104,6 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
|
||||
scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
|
||||
|
||||
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
|
||||
/// `B: A`. This is used to compute the universal regions that are required
|
||||
/// to outlive a given SCC.
|
||||
rev_scc_graph: OnceCell<ReverseSccGraph>,
|
||||
|
||||
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
|
||||
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
|
||||
|
||||
/// Records the member constraints that we applied to each scc.
|
||||
/// This is useful for error reporting. Once constraint
|
||||
/// propagation is done, this vector is sorted according to
|
||||
/// `member_region_scc`.
|
||||
member_constraints_applied: Vec<AppliedMemberConstraint>,
|
||||
|
||||
/// Map universe indexes to information on why we created it.
|
||||
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
|
||||
|
||||
|
|
@ -150,32 +120,6 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
}
|
||||
|
||||
/// Each time that `apply_member_constraint` is successful, it appends
|
||||
/// one of these structs to the `member_constraints_applied` field.
|
||||
/// This is used in error reporting to trace out what happened.
|
||||
///
|
||||
/// The way that `apply_member_constraint` works is that it effectively
|
||||
/// adds a new lower bound to the SCC it is analyzing: so you wind up
|
||||
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
|
||||
/// minimal viable option.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AppliedMemberConstraint {
|
||||
/// The SCC that was affected. (The "member region".)
|
||||
///
|
||||
/// The vector if `AppliedMemberConstraint` elements is kept sorted
|
||||
/// by this field.
|
||||
pub(crate) member_region_scc: ConstraintSccIndex,
|
||||
|
||||
/// The "best option" that `apply_member_constraint` found -- this was
|
||||
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
|
||||
pub(crate) min_choice: ty::RegionVid,
|
||||
|
||||
/// The "member constraint index" -- we can find out details about
|
||||
/// the constraint from
|
||||
/// `set.member_constraints[member_constraint_index]`.
|
||||
pub(crate) member_constraint_index: NllMemberConstraintIndex,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RegionDefinition<'tcx> {
|
||||
/// What kind of variable is this -- a free region? existential
|
||||
|
|
@ -268,7 +212,6 @@ enum Trace<'a, 'tcx> {
|
|||
StartRegion,
|
||||
FromGraph(&'a OutlivesConstraint<'tcx>),
|
||||
FromStatic(RegionVid),
|
||||
FromMember(RegionVid, RegionVid, Span),
|
||||
NotVisited,
|
||||
}
|
||||
|
||||
|
|
@ -363,7 +306,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
member_constraints,
|
||||
} = lowered_constraints;
|
||||
|
||||
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
|
||||
|
|
@ -385,9 +327,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
scc_values.merge_liveness(scc, region, &liveness_constraints);
|
||||
}
|
||||
|
||||
let member_constraints =
|
||||
Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
|
||||
|
||||
let mut result = Self {
|
||||
definitions,
|
||||
liveness_constraints,
|
||||
|
|
@ -395,9 +334,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
constraint_graph,
|
||||
constraint_sccs,
|
||||
scc_annotations,
|
||||
rev_scc_graph: OnceCell::new(),
|
||||
member_constraints,
|
||||
member_constraints_applied: Vec::new(),
|
||||
universe_causes,
|
||||
scc_values,
|
||||
type_tests,
|
||||
|
|
@ -550,19 +486,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_values.placeholders_contained_in(scc)
|
||||
}
|
||||
|
||||
/// Once region solving has completed, this function will return the member constraints that
|
||||
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
|
||||
pub(crate) fn applied_member_constraints(
|
||||
&self,
|
||||
scc: ConstraintSccIndex,
|
||||
) -> &[AppliedMemberConstraint] {
|
||||
binary_search_util::binary_search_slice(
|
||||
&self.member_constraints_applied,
|
||||
|applied| applied.member_region_scc,
|
||||
&scc,
|
||||
)
|
||||
}
|
||||
|
||||
/// Performs region inference and report errors if we see any
|
||||
/// unsatisfiable constraints. If this is a closure, returns the
|
||||
/// region requirements to propagate to our creator, if any.
|
||||
|
|
@ -607,12 +530,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
debug!(?errors_buffer);
|
||||
|
||||
if errors_buffer.is_empty() {
|
||||
self.check_member_constraints(infcx, &mut errors_buffer);
|
||||
}
|
||||
|
||||
debug!(?errors_buffer);
|
||||
|
||||
let outlives_requirements = outlives_requirements.unwrap_or_default();
|
||||
|
||||
if outlives_requirements.is_empty() {
|
||||
|
|
@ -642,154 +559,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
});
|
||||
|
||||
// To propagate constraints, we walk the DAG induced by the
|
||||
// SCC. For each SCC, we visit its successors and compute
|
||||
// SCC. For each SCC `A`, we visit its successors and compute
|
||||
// their values, then we union all those values to get our
|
||||
// own.
|
||||
for scc in self.constraint_sccs.all_sccs() {
|
||||
self.compute_value_for_scc(scc);
|
||||
}
|
||||
|
||||
// Sort the applied member constraints so we can binary search
|
||||
// through them later.
|
||||
self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
|
||||
}
|
||||
|
||||
/// Computes the value of the SCC `scc_a`, which has not yet been
|
||||
/// computed, by unioning the values of its successors.
|
||||
/// Assumes that all successors have been computed already
|
||||
/// (which is assured by iterating over SCCs in dependency order).
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
|
||||
// Walk each SCC `B` such that `A: B`...
|
||||
for &scc_b in self.constraint_sccs.successors(scc_a) {
|
||||
debug!(?scc_b);
|
||||
self.scc_values.add_region(scc_a, scc_b);
|
||||
}
|
||||
|
||||
// Now take member constraints into account.
|
||||
let member_constraints = Rc::clone(&self.member_constraints);
|
||||
for m_c_i in member_constraints.indices(scc_a) {
|
||||
self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
|
||||
}
|
||||
|
||||
debug!(value = ?self.scc_values.region_value_str(scc_a));
|
||||
}
|
||||
|
||||
/// Invoked for each `R0 member of [R1..Rn]` constraint.
|
||||
///
|
||||
/// `scc` is the SCC containing R0, and `choice_regions` are the
|
||||
/// `R1..Rn` regions -- they are always known to be universal
|
||||
/// regions (and if that's not true, we just don't attempt to
|
||||
/// enforce the constraint).
|
||||
///
|
||||
/// The current value of `scc` at the time the method is invoked
|
||||
/// is considered a *lower bound*. If possible, we will modify
|
||||
/// the constraint to set it equal to one of the option regions.
|
||||
/// If we make any changes, returns true, else false.
|
||||
///
|
||||
/// This function only adds the member constraints to the region graph,
|
||||
/// it does not check them. They are later checked in
|
||||
/// `check_member_constraints` after the region graph has been computed.
|
||||
#[instrument(skip(self, member_constraint_index), level = "debug")]
|
||||
fn apply_member_constraint(
|
||||
&mut self,
|
||||
scc: ConstraintSccIndex,
|
||||
member_constraint_index: NllMemberConstraintIndex,
|
||||
choice_regions: &[ty::RegionVid],
|
||||
) {
|
||||
// Create a mutable vector of the options. We'll try to winnow
|
||||
// them down.
|
||||
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
|
||||
|
||||
// Convert to the SCC representative: sometimes we have inference
|
||||
// variables in the member constraint that wind up equated with
|
||||
// universal regions. The scc representative is the minimal numbered
|
||||
// one from the corresponding scc so it will be the universal region
|
||||
// if one exists.
|
||||
for c_r in &mut choice_regions {
|
||||
let scc = self.constraint_sccs.scc(*c_r);
|
||||
*c_r = self.scc_representative(scc);
|
||||
}
|
||||
|
||||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
return;
|
||||
}
|
||||
|
||||
// The existing value for `scc` is a lower-bound. This will
|
||||
// consist of some set `{P} + {LB}` of points `{P}` and
|
||||
// lower-bound free regions `{LB}`. As each choice region `O`
|
||||
// is a free region, it will outlive the points. But we can
|
||||
// only consider the option `O` if `O: LB`.
|
||||
choice_regions.retain(|&o_r| {
|
||||
self.scc_values
|
||||
.universal_regions_outlived_by(scc)
|
||||
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
|
||||
});
|
||||
debug!(?choice_regions, "after lb");
|
||||
|
||||
// Now find all the *upper bounds* -- that is, each UB is a
|
||||
// free region that must outlive the member region `R0` (`UB:
|
||||
// R0`). Therefore, we need only keep an option `O` if `UB: O`
|
||||
// for all UB.
|
||||
let universal_region_relations = &self.universal_region_relations;
|
||||
for ub in self.reverse_scc_graph().upper_bounds(scc) {
|
||||
debug!(?ub);
|
||||
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
|
||||
}
|
||||
debug!(?choice_regions, "after ub");
|
||||
|
||||
// At this point we can pick any member of `choice_regions` and would like to choose
|
||||
// it to be a small as possible. To avoid potential non-determinism we will pick the
|
||||
// smallest such choice.
|
||||
//
|
||||
// Because universal regions are only partially ordered (i.e, not every two regions are
|
||||
// comparable), we will ignore any region that doesn't compare to all others when picking
|
||||
// the minimum choice.
|
||||
//
|
||||
// For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
|
||||
// `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
|
||||
// `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
|
||||
let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
|
||||
choice_regions.iter().all(|&r2| {
|
||||
self.universal_region_relations.outlives(r1, r2)
|
||||
|| self.universal_region_relations.outlives(r2, r1)
|
||||
})
|
||||
});
|
||||
// Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
|
||||
let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
|
||||
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
|
||||
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
|
||||
match (r1_outlives_r2, r2_outlives_r1) {
|
||||
(true, true) => r1.min(r2),
|
||||
(true, false) => r2,
|
||||
(false, true) => r1,
|
||||
(false, false) => bug!("incomparable regions in total order"),
|
||||
for scc_a in self.constraint_sccs.all_sccs() {
|
||||
// Walk each SCC `B` such that `A: B`...
|
||||
for &scc_b in self.constraint_sccs.successors(scc_a) {
|
||||
debug!(?scc_b);
|
||||
self.scc_values.add_region(scc_a, scc_b);
|
||||
}
|
||||
}) else {
|
||||
debug!("no unique minimum choice");
|
||||
return;
|
||||
};
|
||||
|
||||
// As we require `'scc: 'min_choice`, we have definitely already computed
|
||||
// its `scc_values` at this point.
|
||||
let min_choice_scc = self.constraint_sccs.scc(min_choice);
|
||||
debug!(?min_choice, ?min_choice_scc);
|
||||
if self.scc_values.add_region(scc, min_choice_scc) {
|
||||
self.member_constraints_applied.push(AppliedMemberConstraint {
|
||||
member_region_scc: scc,
|
||||
min_choice,
|
||||
member_constraint_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if all the elements in the value of `scc_b` are nameable
|
||||
/// Returns `true` if all the placeholders in the value of `scc_b` are nameable
|
||||
/// in `scc_a`. Used during constraint propagation, and only once
|
||||
/// the value of `scc_b` has been computed.
|
||||
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
|
||||
self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b])
|
||||
fn can_name_all_placeholders(
|
||||
&self,
|
||||
scc_a: ConstraintSccIndex,
|
||||
scc_b: ConstraintSccIndex,
|
||||
) -> bool {
|
||||
self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b])
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
|
|
@ -829,7 +619,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
|
||||
// Type-test failed. Report the error.
|
||||
let erased_generic_kind = infcx.tcx.erase_regions(type_test.generic_kind);
|
||||
let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind);
|
||||
|
||||
// Skip duplicate-ish errors.
|
||||
if deduplicate_errors.insert((
|
||||
|
|
@ -1178,16 +968,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
return true;
|
||||
}
|
||||
|
||||
let fr_static = self.universal_regions().fr_static;
|
||||
|
||||
// If we are checking that `'sup: 'sub`, and `'sub` contains
|
||||
// some placeholder that `'sup` cannot name, then this is only
|
||||
// true if `'sup` outlives static.
|
||||
if !self.universe_compatible(sub_region_scc, sup_region_scc) {
|
||||
//
|
||||
// Avoid infinite recursion if `sub_region` is already `'static`
|
||||
if sub_region != fr_static
|
||||
&& !self.can_name_all_placeholders(sup_region_scc, sub_region_scc)
|
||||
{
|
||||
debug!(
|
||||
"sub universe `{sub_region_scc:?}` is not nameable \
|
||||
by super `{sup_region_scc:?}`, promoting to static",
|
||||
);
|
||||
|
||||
return self.eval_outlives(sup_region, self.universal_regions().fr_static);
|
||||
return self.eval_outlives(sup_region, fr_static);
|
||||
}
|
||||
|
||||
// Both the `sub_region` and `sup_region` consist of the union
|
||||
|
|
@ -1487,11 +1283,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
{
|
||||
debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
|
||||
|
||||
let blame_span_category = self.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
shorter_fr,
|
||||
);
|
||||
let blame_constraint = self
|
||||
.best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr)
|
||||
.0;
|
||||
|
||||
// Grow `shorter_fr` until we find some non-local regions. (We
|
||||
// always will.) We'll call them `shorter_fr+` -- they're ever
|
||||
|
|
@ -1504,8 +1298,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
|
||||
subject: ClosureOutlivesSubject::Region(fr_minus),
|
||||
outlived_free_region: fr,
|
||||
blame_span: blame_span_category.1.span,
|
||||
category: blame_span_category.0,
|
||||
blame_span: blame_constraint.cause.span,
|
||||
category: blame_constraint.category,
|
||||
});
|
||||
}
|
||||
return RegionRelationCheckResult::Propagated;
|
||||
|
|
@ -1544,118 +1338,67 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, infcx, errors_buffer))]
|
||||
fn check_member_constraints(
|
||||
pub(crate) fn constraint_path_between_regions(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
errors_buffer: &mut RegionErrors<'tcx>,
|
||||
) {
|
||||
let member_constraints = Rc::clone(&self.member_constraints);
|
||||
for m_c_i in member_constraints.all_indices() {
|
||||
debug!(?m_c_i);
|
||||
let m_c = &member_constraints[m_c_i];
|
||||
let member_region_vid = m_c.member_region_vid;
|
||||
debug!(
|
||||
?member_region_vid,
|
||||
value = ?self.region_value_str(member_region_vid),
|
||||
);
|
||||
let choice_regions = member_constraints.choice_regions(m_c_i);
|
||||
debug!(?choice_regions);
|
||||
|
||||
// Did the member region wind up equal to any of the option regions?
|
||||
if let Some(o) =
|
||||
choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
|
||||
{
|
||||
debug!("evaluated as equal to {:?}", o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not, report an error.
|
||||
let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
|
||||
errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
|
||||
span: m_c.definition_span,
|
||||
hidden_ty: m_c.hidden_ty,
|
||||
key: m_c.key,
|
||||
member_region,
|
||||
});
|
||||
from_region: RegionVid,
|
||||
to_region: RegionVid,
|
||||
) -> Option<Vec<OutlivesConstraint<'tcx>>> {
|
||||
if from_region == to_region {
|
||||
bug!("Tried to find a path between {from_region:?} and itself!");
|
||||
}
|
||||
}
|
||||
|
||||
/// We have a constraint `fr1: fr2` that is not satisfied, where
|
||||
/// `fr2` represents some universal region. Here, `r` is some
|
||||
/// region where we know that `fr1: r` and this function has the
|
||||
/// job of determining whether `r` is "to blame" for the fact that
|
||||
/// `fr1: fr2` is required.
|
||||
///
|
||||
/// This is true under two conditions:
|
||||
///
|
||||
/// - `r == fr2`
|
||||
/// - `fr2` is `'static` and `r` is some placeholder in a universe
|
||||
/// that cannot be named by `fr1`; in that case, we will require
|
||||
/// that `fr1: 'static` because it is the only way to `fr1: r` to
|
||||
/// be satisfied. (See `add_incompatible_universe`.)
|
||||
pub(crate) fn provides_universal_region(
|
||||
&self,
|
||||
r: RegionVid,
|
||||
fr1: RegionVid,
|
||||
fr2: RegionVid,
|
||||
) -> bool {
|
||||
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
|
||||
let result = {
|
||||
r == fr2 || {
|
||||
fr2 == self.universal_regions().fr_static && self.cannot_name_placeholder(fr1, r)
|
||||
}
|
||||
};
|
||||
debug!("provides_universal_region: result = {:?}", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// If `r2` represents a placeholder region, then this returns
|
||||
/// `true` if `r1` cannot name that placeholder in its
|
||||
/// value; otherwise, returns `false`.
|
||||
pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
|
||||
match self.definitions[r2].origin {
|
||||
NllRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
let r1_universe = self.definitions[r1].universe;
|
||||
debug!(
|
||||
"cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
|
||||
placeholder
|
||||
);
|
||||
r1_universe.cannot_name(placeholder.universe)
|
||||
}
|
||||
|
||||
NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
|
||||
pub(crate) fn find_outlives_blame_span(
|
||||
&self,
|
||||
fr1: RegionVid,
|
||||
fr1_origin: NllRegionVariableOrigin,
|
||||
fr2: RegionVid,
|
||||
) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
|
||||
let BlameConstraint { category, cause, .. } = self
|
||||
.best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
|
||||
.0;
|
||||
(category, cause)
|
||||
self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0)
|
||||
}
|
||||
|
||||
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
||||
/// `to_region`. The paths are accumulated into the vector
|
||||
/// `results`. The paths are stored as a series of
|
||||
/// `ConstraintIndex` values -- in other words, a list of *edges*.
|
||||
/// an edge `'a -> 'b`) to find a path from `from_region` to
|
||||
/// `to_region`.
|
||||
///
|
||||
/// Returns: a series of constraints as well as the region `R`
|
||||
/// that passed the target test.
|
||||
/// If `include_static_outlives_all` is `true`, then the synthetic
|
||||
/// outlives constraints `'static -> a` for every region `a` are
|
||||
/// considered in the search, otherwise they are ignored.
|
||||
#[instrument(skip(self, target_test), ret)]
|
||||
pub(crate) fn find_constraint_paths_between_regions(
|
||||
pub(crate) fn constraint_path_to(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
include_placeholder_static: bool,
|
||||
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
|
||||
self.find_constraint_path_between_regions_inner(
|
||||
true,
|
||||
from_region,
|
||||
&target_test,
|
||||
include_placeholder_static,
|
||||
)
|
||||
.or_else(|| {
|
||||
self.find_constraint_path_between_regions_inner(
|
||||
false,
|
||||
from_region,
|
||||
&target_test,
|
||||
include_placeholder_static,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// The constraints we get from equating the hidden type of each use of an opaque
|
||||
/// with its final concrete type may end up getting preferred over other, potentially
|
||||
/// longer constraint paths.
|
||||
///
|
||||
/// Given that we compute the final concrete type by relying on this existing constraint
|
||||
/// path, this can easily end up hiding the actual reason for why we require these regions
|
||||
/// to be equal.
|
||||
///
|
||||
/// To handle this, we first look at the path while ignoring these constraints and then
|
||||
/// retry while considering them. This is not perfect, as the `from_region` may have already
|
||||
/// been partially related to its argument region, so while we rely on a member constraint
|
||||
/// to get a complete path, the most relevant step of that path already existed before then.
|
||||
fn find_constraint_path_between_regions_inner(
|
||||
&self,
|
||||
ignore_opaque_type_constraints: bool,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
include_placeholder_static: bool,
|
||||
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
|
||||
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
|
||||
context[from_region] = Trace::StartRegion;
|
||||
|
|
@ -1670,7 +1413,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
while let Some(r) = deque.pop_front() {
|
||||
debug!(
|
||||
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
|
||||
"constraint_path_to: from_region={:?} r={:?} value={}",
|
||||
from_region,
|
||||
r,
|
||||
self.region_value_str(r),
|
||||
|
|
@ -1704,20 +1447,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
result.push(c);
|
||||
}
|
||||
|
||||
Trace::FromMember(sup, sub, span) => {
|
||||
let c = OutlivesConstraint {
|
||||
sup,
|
||||
sub,
|
||||
locations: Locations::All(span),
|
||||
span,
|
||||
category: ConstraintCategory::OpaqueType,
|
||||
variance_info: ty::VarianceDiagInfo::default(),
|
||||
from_closure: false,
|
||||
};
|
||||
p = c.sup;
|
||||
result.push(c);
|
||||
}
|
||||
|
||||
Trace::StartRegion => {
|
||||
result.reverse();
|
||||
return Some((result, r));
|
||||
|
|
@ -1756,23 +1485,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
|
||||
// This loop can be hot.
|
||||
for constraint in edges {
|
||||
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
|
||||
debug!("Ignoring illegal universe constraint: {constraint:?}");
|
||||
continue;
|
||||
match constraint.category {
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_)
|
||||
if !include_placeholder_static =>
|
||||
{
|
||||
debug!("Ignoring illegal placeholder constraint: {constraint:?}");
|
||||
continue;
|
||||
}
|
||||
ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => {
|
||||
debug!("Ignoring member constraint: {constraint:?}");
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
handle_trace(constraint.sub, Trace::FromGraph(constraint));
|
||||
}
|
||||
}
|
||||
|
||||
// Member constraints can also give rise to `'r: 'x` edges that
|
||||
// were not part of the graph initially, so watch out for those.
|
||||
// (But they are extremely rare; this loop is very cold.)
|
||||
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
|
||||
let sub = constraint.min_choice;
|
||||
let p_c = &self.member_constraints[constraint.member_constraint_index];
|
||||
handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -1783,37 +1513,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
self.constraint_path_to(fr1, |r| {
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
self.liveness_constraints.is_live_at(r, location)
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find that, we may find some `r` such that
|
||||
// `fr1: r` and `r` is a placeholder from some universe
|
||||
// `fr1` cannot name. This would force `fr1` to be
|
||||
// `'static`.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.cannot_name_placeholder(fr1, r)
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find THAT, it may be that `fr1` is a
|
||||
// placeholder that cannot "fit" into its SCC. In that
|
||||
// case, there should be some `r` where `fr1: r` and `fr1` is a
|
||||
// placeholder that `r` cannot name. We can blame that
|
||||
// edge.
|
||||
//
|
||||
// Remember that if `R1: R2`, then the universe of R1
|
||||
// must be able to name the universe of R2, because R2 will
|
||||
// be at least `'empty(Universe(R2))`, and `R1` must be at
|
||||
// larger than that.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.cannot_name_placeholder(r, fr1)
|
||||
})
|
||||
})
|
||||
.map(|(_path, r)| r)
|
||||
.unwrap()
|
||||
}, true).unwrap().1
|
||||
}
|
||||
|
||||
/// Get the region outlived by `longer_fr` and live at `element`.
|
||||
|
|
@ -1857,22 +1560,38 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// creating a constraint path that forces `R` to outlive
|
||||
/// `from_region`, and then finding the best choices within that
|
||||
/// path to blame.
|
||||
#[instrument(level = "debug", skip(self, target_test))]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn best_blame_constraint(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
from_region_origin: NllRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
to_region: RegionVid,
|
||||
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
|
||||
// Find all paths
|
||||
let (path, target_region) = self
|
||||
.find_constraint_paths_between_regions(from_region, target_test)
|
||||
.or_else(|| {
|
||||
self.find_constraint_paths_between_regions(from_region, |r| {
|
||||
self.cannot_name_placeholder(from_region, r)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
assert!(from_region != to_region, "Trying to blame a region for itself!");
|
||||
|
||||
let path = self.constraint_path_between_regions(from_region, to_region).unwrap();
|
||||
|
||||
// If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`,
|
||||
// redirect search towards `'unnameable`.
|
||||
let due_to_placeholder_outlives = path.iter().find_map(|c| {
|
||||
if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = c.category {
|
||||
Some(unnameable)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
// Edge case: it's possible that `'from_region` is an unnameable placeholder.
|
||||
let path = if let Some(unnameable) = due_to_placeholder_outlives
|
||||
&& unnameable != from_region
|
||||
{
|
||||
// We ignore the extra edges due to unnameable placeholders to get
|
||||
// an explanation that was present in the original constraint graph.
|
||||
self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
debug!(
|
||||
"path={:#?}",
|
||||
path.iter()
|
||||
|
|
@ -1939,10 +1658,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
//
|
||||
// and here we prefer to blame the source (the y = x statement).
|
||||
let blame_source = match from_region_origin {
|
||||
NllRegionVariableOrigin::FreeRegion
|
||||
| NllRegionVariableOrigin::Existential { from_forall: false, name: _ } => true,
|
||||
NllRegionVariableOrigin::Placeholder(_)
|
||||
| NllRegionVariableOrigin::Existential { from_forall: true, name: _ } => false,
|
||||
NllRegionVariableOrigin::FreeRegion => true,
|
||||
NllRegionVariableOrigin::Placeholder(_) => false,
|
||||
// `'existential: 'whatever` never results in a region error by itself.
|
||||
// We may always infer it to `'static` afterall. This means while an error
|
||||
// path may go through an existential, these existentials are never the
|
||||
// `from_region`.
|
||||
NllRegionVariableOrigin::Existential { name: _ } => {
|
||||
unreachable!("existentials can outlive everything")
|
||||
}
|
||||
};
|
||||
|
||||
// To pick a constraint to blame, we organize constraints by how interesting we expect them
|
||||
|
|
@ -1975,7 +1699,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
ConstraintCategory::Cast {
|
||||
unsize_to: Some(unsize_ty),
|
||||
is_implicit_coercion: true,
|
||||
} if target_region == self.universal_regions().fr_static
|
||||
} if to_region == self.universal_regions().fr_static
|
||||
// Mirror the note's condition, to minimize how often this diverts blame.
|
||||
&& let ty::Adt(_, args) = unsize_ty.kind()
|
||||
&& args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait()))
|
||||
|
|
@ -2012,9 +1736,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// `BoringNoLocation` constraints can point to user-written code, but are less
|
||||
// specific, and are not used for relations that would make sense to blame.
|
||||
ConstraintCategory::BoringNoLocation => 6,
|
||||
// Do not blame internal constraints.
|
||||
ConstraintCategory::IllegalUniverse => 7,
|
||||
ConstraintCategory::Internal => 8,
|
||||
// Do not blame internal constraints if we can avoid it. Never blame
|
||||
// the `'region: 'static` constraints introduced by placeholder outlives.
|
||||
ConstraintCategory::Internal => 7,
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 8,
|
||||
};
|
||||
|
||||
debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}");
|
||||
|
|
@ -2050,6 +1775,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
path[best_choice]
|
||||
};
|
||||
|
||||
assert!(
|
||||
!matches!(
|
||||
best_constraint.category,
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_)
|
||||
),
|
||||
"Illegal placeholder constraint blamed; should have redirected to other region relation"
|
||||
);
|
||||
|
||||
let blame_constraint = BlameConstraint {
|
||||
category: best_constraint.category,
|
||||
from_closure: best_constraint.from_closure,
|
||||
|
|
@ -2099,11 +1832,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
&self.constraint_sccs
|
||||
}
|
||||
|
||||
/// Access to the region graph, built from the outlives constraints.
|
||||
pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> {
|
||||
self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
|
||||
}
|
||||
|
||||
/// Returns the representative `RegionVid` for a given SCC.
|
||||
/// See `RegionTracker` for how a region variable ID is chosen.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,299 +0,0 @@
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::{
|
||||
self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
|
||||
TypeVisitableExt, fold_regions,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::opaque_types::{
|
||||
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::RegionInferenceContext;
|
||||
use crate::BorrowCheckRootCtxt;
|
||||
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
|
||||
use crate::universal_regions::RegionClassification;
|
||||
|
||||
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
|
||||
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
|
||||
LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Resolve any opaque types that were encountered while borrow checking
|
||||
/// this item. This is then used to get the type in the `type_of` query.
|
||||
///
|
||||
/// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
|
||||
/// This is lowered to give HIR something like
|
||||
///
|
||||
/// type f<'a>::_Return<'_x> = impl Sized + '_x;
|
||||
/// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
|
||||
///
|
||||
/// When checking the return type record the type from the return and the
|
||||
/// type used in the return value. In this case they might be `_Return<'1>`
|
||||
/// and `&'2 i32` respectively.
|
||||
///
|
||||
/// Once we to this method, we have completed region inference and want to
|
||||
/// call `infer_opaque_definition_from_instantiation` to get the inferred
|
||||
/// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
|
||||
/// compares lifetimes directly, so we need to map the inference variables
|
||||
/// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
|
||||
///
|
||||
/// First we map the regions in the generic parameters `_Return<'1>` to
|
||||
/// their `external_name` giving `_Return<'a>`. This step is a bit involved.
|
||||
/// See the [rustc-dev-guide chapter] for more info.
|
||||
///
|
||||
/// Then we map all the lifetimes in the concrete type to an equal
|
||||
/// universal region that occurs in the opaque type's args, in this case
|
||||
/// this would result in `&'a i32`. We only consider regions in the args
|
||||
/// in case there is an equal region that does not. For example, this should
|
||||
/// be allowed:
|
||||
/// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
|
||||
///
|
||||
/// This will then allow `infer_opaque_definition_from_instantiation` to
|
||||
/// determine that `_Return<'_x> = &'_x i32`.
|
||||
///
|
||||
/// There's a slight complication around closures. Given
|
||||
/// `fn f<'a: 'a>() { || {} }` the closure's type is something like
|
||||
/// `f::<'a>::{{closure}}`. The region parameter from f is essentially
|
||||
/// ignored by type checking so ends up being inferred to an empty region.
|
||||
/// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
|
||||
/// which has no `external_name` in which case we use `'{erased}` as the
|
||||
/// region to pass to `infer_opaque_definition_from_instantiation`.
|
||||
///
|
||||
/// [rustc-dev-guide chapter]:
|
||||
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
|
||||
#[instrument(level = "debug", skip(self, root_cx, infcx))]
|
||||
pub(crate) fn infer_opaque_types(
|
||||
&self,
|
||||
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
|
||||
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
|
||||
let mut errors = Vec::new();
|
||||
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
|
||||
FxIndexMap::default();
|
||||
|
||||
for (opaque_type_key, concrete_type) in opaque_ty_decls {
|
||||
debug!(?opaque_type_key, ?concrete_type);
|
||||
|
||||
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
|
||||
vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
|
||||
|
||||
let opaque_type_key =
|
||||
opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
|
||||
// Use the SCC representative instead of directly using `region`.
|
||||
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
|
||||
let scc = self.constraint_sccs.scc(region.as_var());
|
||||
let vid = self.scc_representative(scc);
|
||||
let named = match self.definitions[vid].origin {
|
||||
// Iterate over all universal regions in a consistent order and find the
|
||||
// *first* equal region. This makes sure that equal lifetimes will have
|
||||
// the same name and simplifies subsequent handling.
|
||||
// See [rustc-dev-guide chapter] § "Semantic lifetime equality".
|
||||
NllRegionVariableOrigin::FreeRegion => self
|
||||
.universal_regions()
|
||||
.universal_regions_iter()
|
||||
.filter(|&ur| {
|
||||
// See [rustc-dev-guide chapter] § "Closure restrictions".
|
||||
!matches!(
|
||||
self.universal_regions().region_classification(ur),
|
||||
Some(RegionClassification::External)
|
||||
)
|
||||
})
|
||||
.find(|&ur| self.universal_region_relations.equal(vid, ur))
|
||||
.map(|ur| self.definitions[ur].external_name.unwrap()),
|
||||
NllRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
|
||||
}
|
||||
NllRegionVariableOrigin::Existential { .. } => None,
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
ty::Region::new_error_with_message(
|
||||
infcx.tcx,
|
||||
concrete_type.span,
|
||||
"opaque type with non-universal region args",
|
||||
)
|
||||
});
|
||||
|
||||
arg_regions.push((vid, named));
|
||||
named
|
||||
});
|
||||
debug!(?opaque_type_key, ?arg_regions);
|
||||
|
||||
let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
|
||||
arg_regions
|
||||
.iter()
|
||||
.find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
|
||||
.map(|&(_, arg_named)| arg_named)
|
||||
.unwrap_or(infcx.tcx.lifetimes.re_erased)
|
||||
});
|
||||
debug!(?concrete_type);
|
||||
|
||||
let ty = match infcx
|
||||
.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type)
|
||||
{
|
||||
Ok(ty) => ty,
|
||||
Err(err) => {
|
||||
errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Sometimes, when the hidden type is an inference variable, it can happen that
|
||||
// the hidden type becomes the opaque type itself. In this case, this was an opaque
|
||||
// usage of the opaque type and we can ignore it. This check is mirrored in typeck's
|
||||
// writeback.
|
||||
if !infcx.next_trait_solver() {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
|
||||
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
|
||||
&& alias_ty.args == opaque_type_key.args
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
root_cx.add_concrete_opaque_type(
|
||||
opaque_type_key.def_id,
|
||||
OpaqueHiddenType { span: concrete_type.span, ty },
|
||||
);
|
||||
|
||||
// Check that all opaque types have the same region parameters if they have the same
|
||||
// non-region parameters. This is necessary because within the new solver we perform
|
||||
// various query operations modulo regions, and thus could unsoundly select some impls
|
||||
// that don't hold.
|
||||
if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
|
||||
infcx.tcx.erase_regions(opaque_type_key),
|
||||
(opaque_type_key, concrete_type.span),
|
||||
) && let Some((arg1, arg2)) = std::iter::zip(
|
||||
prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
|
||||
opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
|
||||
)
|
||||
.find(|(arg1, arg2)| arg1 != arg2)
|
||||
{
|
||||
errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
|
||||
LifetimeMismatchOpaqueParam {
|
||||
arg: arg1,
|
||||
prev: arg2,
|
||||
span: prev_span,
|
||||
prev_span: concrete_type.span,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
errors
|
||||
}
|
||||
|
||||
/// Map the regions in the type to named regions. This is similar to what
|
||||
/// `infer_opaque_types` does, but can infer any universal region, not only
|
||||
/// ones from the args for the opaque type. It also doesn't double check
|
||||
/// that the regions produced are in fact equal to the named region they are
|
||||
/// replaced with. This is fine because this function is only to improve the
|
||||
/// region names in error messages.
|
||||
///
|
||||
/// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
|
||||
/// lax with mapping region vids that are *shorter* than a universal region to
|
||||
/// that universal region. This is useful for member region constraints since
|
||||
/// we want to suggest a universal region name to capture even if it's technically
|
||||
/// not equal to the error region.
|
||||
pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
fold_regions(tcx, ty, |region, _| match region.kind() {
|
||||
ty::ReVar(vid) => {
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
|
||||
// Special handling of higher-ranked regions.
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
|
||||
// If the region contains a single placeholder then they're equal.
|
||||
Some((0, placeholder)) => {
|
||||
return ty::Region::new_placeholder(tcx, placeholder);
|
||||
}
|
||||
|
||||
// Fallback: this will produce a cryptic error message.
|
||||
_ => return region,
|
||||
}
|
||||
}
|
||||
|
||||
// Find something that we can name
|
||||
let upper_bound = self.approx_universal_upper_bound(vid);
|
||||
if let Some(universal_region) = self.definitions[upper_bound].external_name {
|
||||
return universal_region;
|
||||
}
|
||||
|
||||
// Nothing exact found, so we pick a named upper bound, if there's only one.
|
||||
// If there's >1 universal region, then we probably are dealing w/ an intersection
|
||||
// region which cannot be mapped back to a universal.
|
||||
// FIXME: We could probably compute the LUB if there is one.
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
let upper_bounds: Vec<_> = self
|
||||
.reverse_scc_graph()
|
||||
.upper_bounds(scc)
|
||||
.filter_map(|vid| self.definitions[vid].external_name)
|
||||
.filter(|r| !r.is_static())
|
||||
.collect();
|
||||
match &upper_bounds[..] {
|
||||
[universal_region] => *universal_region,
|
||||
_ => region,
|
||||
}
|
||||
}
|
||||
_ => region,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[extension(pub trait InferCtxtExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given the fully resolved, instantiated type for an opaque
|
||||
/// type, i.e., the value of an inference variable like C1 or C2
|
||||
/// (*), computes the "definition type" for an opaque type
|
||||
/// definition -- that is, the inferred value of `Foo1<'x>` or
|
||||
/// `Foo2<'x>` that we would conceptually use in its definition:
|
||||
/// ```ignore (illustrative)
|
||||
/// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA
|
||||
/// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB
|
||||
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
|
||||
/// ```
|
||||
/// Note that these values are defined in terms of a distinct set of
|
||||
/// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
|
||||
/// purpose of this function is to do that translation.
|
||||
///
|
||||
/// (*) C1 and C2 were introduced in the comments on
|
||||
/// `register_member_constraints`. Read that comment for more context.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `def_id`, the `impl Trait` type
|
||||
/// - `args`, the args used to instantiate this opaque type
|
||||
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
|
||||
/// `opaque_defn.concrete_ty`
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn infer_opaque_definition_from_instantiation(
|
||||
&self,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
instantiated_ty: OpaqueHiddenType<'tcx>,
|
||||
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
|
||||
check_opaque_type_parameter_valid(
|
||||
self,
|
||||
opaque_type_key,
|
||||
instantiated_ty.span,
|
||||
DefiningScopeKind::MirBorrowck,
|
||||
)?;
|
||||
|
||||
let definition_ty = instantiated_ty
|
||||
.remap_generic_params_to_declaration_params(
|
||||
opaque_type_key,
|
||||
self.tcx,
|
||||
DefiningScopeKind::MirBorrowck,
|
||||
)
|
||||
.ty;
|
||||
|
||||
definition_ty.error_reported()?;
|
||||
Ok(definition_ty)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue