Merge ref '73e6c9ebd9' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 73e6c9ebd9
Filtered ref: e8bb3cae4cd2b04bdc252cdf79102717db2b2d8d
Upstream diff: 32e7a4b92b...73e6c9ebd9

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
Jakub Beránek 2025-11-02 14:45:26 +01:00
commit 3c9656c4f4
No known key found for this signature in database
GPG key ID: 909CD0D26483516B
10852 changed files with 315593 additions and 166924 deletions

View file

@ -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

View file

@ -1,5 +1,5 @@
name: Documentation problem
description: Create a report for a documentation problem.
description: Report an issue with documentation content.
labels: ["A-docs"]
body:
- type: markdown
@ -19,20 +19,20 @@ body:
- [The Rustonomicon](https://github.com/rust-lang/nomicon/issues)
- [The Embedded Book](https://github.com/rust-embedded/book/issues)
All other documentation issues should be filed here.
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the rustdoc issue template instead.
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
All other documentation issues should be filed here.
- type: textarea
id: location
attributes:
label: Location
label: Location (URL)
validations:
required: true
required: true
- type: textarea
id: summary
attributes:
label: Summary
validations:
required: true
required: true

View file

@ -51,6 +51,7 @@ If the feature is changed later, please add those PRs here as well.
(Remember to update the `S-tracking-*` label when checking boxes.)
- [ ] ACP: rust-lang/libs-team#...
- [ ] Implementation: #...
- [ ] Final comment period (FCP)[^1]
- [ ] Stabilization PR

View file

@ -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
@ -159,9 +159,6 @@ jobs:
- name: show the current environment
run: src/ci/scripts/dump-environment.sh
- name: install rust
run: src/ci/scripts/install-rust.sh
- name: install awscli
run: src/ci/scripts/install-awscli.sh
@ -249,6 +246,8 @@ jobs:
run: src/ci/scripts/create-doc-artifacts.sh
- name: print disk usage
# We also want to know the disk usage when the job fails.
if: always()
run: |
echo "disk usage:"
df -h
@ -316,7 +315,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.

View file

@ -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

View file

@ -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

View file

@ -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
.gitmodules vendored
View file

@ -25,7 +25,7 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
branch = rustc/20.1-2025-07-13
branch = rustc/21.1-2025-08-01
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

View file

@ -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>
@ -426,6 +427,7 @@ Marcell Pardavi <marcell.pardavi@gmail.com>
Marco Ieni <11428655+MarcoIeni@users.noreply.github.com>
Marcus Klaas de Vries <mail@marcusklaas.nl>
Margaret Meyerhofer <mmeyerho@andrew.cmu.edu> <mmeyerho@andrew>
Marijn Schouten <mhkbst@gmail.com> <hkBst@users.noreply.github.com>
Mark Mansi <markm@cs.wisc.edu>
Mark Mansi <markm@cs.wisc.edu> <m.mim95@gmail.com>
Mark Rousskov <mark.simulacrum@gmail.com>
@ -597,6 +599,7 @@ Sam Radhakrishnan <sk09idm@gmail.com>
Samuel Tardieu <sam@rfc1149.net>
Santiago Pastorino <spastorino@gmail.com>
Santiago Pastorino <spastorino@gmail.com> <santiago@wyeworks.com>
Sasha Pourcelot <sasha.pourcelot@protonmail.com> Sasha <sasha.pourcelot@protonmail.com>
Scott McMurray <scottmcm@users.noreply.github.com>
Scott McMurray <scottmcm@users.noreply.github.com> <smcmurray@acm.org>
Scott Olson <scott@solson.me> Scott Olson <scott@scott-olson.org>
@ -607,6 +610,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>
@ -676,6 +680,7 @@ Valerii Lashmanov <vflashm@gmail.com>
Vitali Haravy <HumaneProgrammer@gmail.com> Vitali Haravy <humaneprogrammer@gmail.com>
Vitaly Shukela <vi0oss@gmail.com>
Waffle Lapkin <waffle.lapkin@gmail.com>
Waffle Lapkin <waffle.lapkin@gmail.com> <Waffle Lapkin>
Waffle Lapkin <waffle.lapkin@gmail.com> <waffle.lapkin@tasking.com>
Weihang Lo <me@weihanglo.tw>
Weihang Lo <me@weihanglo.tw> <weihanglo@users.noreply.github.com>

View file

@ -31,8 +31,8 @@ bootstrapping, the compiler architecture, source code representation, and more.
## [Getting help](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions)
There are many ways you can get help when you're stuck. Rust has many platforms for this:
[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on
There are many ways you can get help when you're stuck. Rust has two platforms for this:
[internals] and [rust-zulip]. It is recommended to ask for help on
the [rust-zulip], but any of these platforms are great ways to seek help and even
find a mentor! You can learn more about asking questions and getting help in the
[Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide].
@ -47,5 +47,4 @@ refer to [this section][contributing-bug-reports] and [open an issue][issue temp
[contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports
[issue template]: https://github.com/rust-lang/rust/issues/new/choose
[internals]: https://internals.rust-lang.org
[rust-discord]: http://discord.gg/rust-lang
[rust-zulip]: https://rust-lang.zulipchat.com

1134
Cargo.lock

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,477 @@
Version 1.91.0 (2025-10-30)
==========================
<a id="1.91.0-Language"></a>
Language
--------
- [Lower pattern bindings in the order they're written and base drop order on primary bindings' order](https://github.com/rust-lang/rust/pull/143764)
- [Stabilize declaration of C-style variadic functions for `sysv64`, `win64`, `efiapi`, and `aapcs` ABIs](https://github.com/rust-lang/rust/pull/144066).
This brings these ABIs in line with the C ABI: variadic functions can be declared in extern blocks but not defined.
- [Add `dangling_pointers_from_locals` lint to warn against dangling pointers from local variables](https://github.com/rust-lang/rust/pull/144322)
- [Upgrade `semicolon_in_expressions_from_macros` from warn to deny](https://github.com/rust-lang/rust/pull/144369)
- [Stabilize LoongArch32 inline assembly](https://github.com/rust-lang/rust/pull/144402)
- [Add warn-by-default `integer_to_ptr_transmutes` lint against integer-to-pointer transmutes](https://github.com/rust-lang/rust/pull/144531)
- [Stabilize `sse4a` and `tbm` target features](https://github.com/rust-lang/rust/pull/144542)
- [Add `target_env = "macabi"` and `target_env = "sim"` cfgs](https://github.com/rust-lang/rust/pull/139451) as replacements for the `target_abi` cfgs with the same values.
<a id="1.91.0-Compiler"></a>
Compiler
--------
- [Don't warn on never-to-any `as` casts as unreachable](https://github.com/rust-lang/rust/pull/144804)
<a id="1.91.0-Platform-Support"></a>
Platform Support
----------------
- [Promote `aarch64-pc-windows-gnullvm` and `x86_64-pc-windows-gnullvm` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/143031)
Note: llvm-tools and MSI installers are missing but will be added in future releases.
- [Promote `aarch64-pc-windows-msvc` to Tier 1](https://github.com/rust-lang/rust/pull/145682)
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.91.0-Libraries"></a>
Libraries
---------
- [Print thread ID in panic message](https://github.com/rust-lang/rust/pull/115746)
- [Fix overly restrictive lifetime in `core::panic::Location::file` return type](https://github.com/rust-lang/rust/pull/132087)
- [Guarantee parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp`](https://github.com/rust-lang/rust/pull/139357)
- [Document assumptions about `Clone` and `Eq` traits](https://github.com/rust-lang/rust/pull/144330/)
- [`std::thread`: Return error if setting thread stack size fails](https://github.com/rust-lang/rust/pull/144210)
This used to panic within the standard library.
<a id="1.91.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`Path::file_prefix`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.file_prefix)
- [`AtomicPtr::fetch_ptr_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_add)
- [`AtomicPtr::fetch_ptr_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_sub)
- [`AtomicPtr::fetch_byte_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_add)
- [`AtomicPtr::fetch_byte_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_sub)
- [`AtomicPtr::fetch_or`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_or)
- [`AtomicPtr::fetch_and`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_and)
- [`AtomicPtr::fetch_xor`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_xor)
- [`{integer}::strict_add`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add)
- [`{integer}::strict_sub`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub)
- [`{integer}::strict_mul`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_mul)
- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div)
- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div_euclid)
- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem)
- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem_euclid)
- [`{integer}::strict_neg`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_neg)
- [`{integer}::strict_shl`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shl)
- [`{integer}::strict_shr`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shr)
- [`{integer}::strict_pow`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_pow)
- [`i{N}::strict_add_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_add_unsigned)
- [`i{N}::strict_sub_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_sub_unsigned)
- [`i{N}::strict_abs`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_abs)
- [`u{N}::strict_add_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add_signed)
- [`u{N}::strict_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub_signed)
- [`PanicHookInfo::payload_as_str`](https://doc.rust-lang.org/stable/std/panic/struct.PanicHookInfo.html#method.payload_as_str)
- [`core::iter::chain`](https://doc.rust-lang.org/stable/core/iter/fn.chain.html)
- [`u{N}::checked_signed_diff`](https://doc.rust-lang.org/stable/std/primitive.u16.html#method.checked_signed_diff)
- [`core::array::repeat`](https://doc.rust-lang.org/stable/core/array/fn.repeat.html)
- [`PathBuf::add_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.add_extension)
- [`PathBuf::with_added_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.with_added_extension)
- [`Duration::from_mins`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_mins)
- [`Duration::from_hours`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_hours)
- [`impl PartialEq<str> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3Cstr%3E-for-PathBuf)
- [`impl PartialEq<String> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3CString%3E-for-PathBuf)
- [`impl PartialEq<str> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3Cstr%3E-for-Path)
- [`impl PartialEq<String> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3CString%3E-for-Path)
- [`impl PartialEq<PathBuf> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPathBuf%3E-for-String)
- [`impl PartialEq<Path> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPath%3E-for-String)
- [`impl PartialEq<PathBuf> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPathBuf%3E-for-str)
- [`impl PartialEq<Path> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPath%3E-for-str)
- [`Ipv4Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.from_octets)
- [`Ipv6Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_octets)
- [`Ipv6Addr::from_segments`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_segments)
- [`impl<T> Default for Pin<Box<T>> where Box<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CBox%3CT%3E%3E)
- [`impl<T> Default for Pin<Rc<T>> where Rc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CRc%3CT%3E%3E)
- [`impl<T> Default for Pin<Arc<T>> where Arc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CArc%3CT%3E%3E)
- [`Cell::as_array_of_cells`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.as_array_of_cells)
- [`u{N}::carrying_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_add)
- [`u{N}::borrowing_sub`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.borrowing_sub)
- [`u{N}::carrying_mul`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul)
- [`u{N}::carrying_mul_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul_add)
- [`BTreeMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.extract_if)
- [`BTreeSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html#method.extract_if)
- [`impl Debug for windows::ffi::EncodeWide<'_>`](https://doc.rust-lang.org/stable/std/os/windows/ffi/struct.EncodeWide.html#impl-Debug-for-EncodeWide%3C'_%3E)
- [`str::ceil_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.ceil_char_boundary)
- [`str::floor_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.floor_char_boundary)
- [`impl Sum for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum-for-Saturating%3Cu32%3E)
- [`impl Sum<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
- [`impl Product for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product-for-Saturating%3Cu32%3E)
- [`impl Product<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
These previously stable APIs are now stable in const contexts:
- [`<[T; N]>::each_ref`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref)
- [`<[T; N]>::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut)
- [`OsString::new`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.new)
- [`PathBuf::new`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.new)
- [`TypeId::of`](https://doc.rust-lang.org/stable/std/any/struct.TypeId.html#method.of)
- [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance.html)
- [`ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance_mut.html)
<a id="1.91.0-Cargo"></a>
Cargo
-----
- 🎉 Stabilize `build.build-dir`.
This config sets the directory where intermediate build artifacts are stored.
These artifacts are produced by Cargo and rustc during the build process.
End users usually won't need to interact with them, and the layout inside
`build-dir` is an implementation detail that may change without notice.
([config doc](https://doc.rust-lang.org/stable/cargo/reference/config.html#buildbuild-dir))
([build cache doc](https://doc.rust-lang.org/stable/cargo/reference/build-cache.html))
[#15833](https://github.com/rust-lang/cargo/pull/15833)
[#15840](https://github.com/rust-lang/cargo/pull/15840)
- The `--target` flag and the `build.target` configuration can now take literal
`"host-tuple"` string, which will internally be substituted by the host
machine's target triple.
[#15838](https://github.com/rust-lang/cargo/pull/15838)
[#16003](https://github.com/rust-lang/cargo/pull/16003)
[#16032](https://github.com/rust-lang/cargo/pull/16032)
<a id="1.91.0-Rustdoc"></a>
Rustdoc
-----
- [In search results, rank doc aliases lower than non-alias items with the same name](https://github.com/rust-lang/rust/pull/145100)
- [Raw pointers now work in type-based search like references](https://github.com/rust-lang/rust/pull/145731). This means you can now search for things like `*const u8 ->`, and additionally functions that take or return raw pointers will now display their signature properly in search results.
<a id="1.91.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Always require coroutine captures to be drop-live](https://github.com/rust-lang/rust/pull/144156)
- [Apple: Always pass SDK root when linking with `cc`, and pass it via `SDKROOT` env var](https://github.com/rust-lang/rust/pull/131477). This should fix linking issues with `rustc` running inside Xcode. Libraries in `/usr/local/lib` may no longer be linked automatically, if you develop or use a crate that relies on this, you should explicitly set `cargo::rustc-link-search=/usr/local/lib` in a `build.rs` script.
- [Relaxed bounds in associated type bound position like in `TraitRef<AssocTy: ?Sized>` are now correctly forbidden](https://github.com/rust-lang/rust/pull/135331)
- [Add unstable `#[sanitize(xyz = "on|off")]` built-in attribute that shadows procedural macros with the same name](https://github.com/rust-lang/rust/pull/142681)
- [Fix the drop checker being more permissive for bindings declared with let-else](https://github.com/rust-lang/rust/pull/143028)
- [Be more strict when parsing attributes, erroring on many invalid attributes](https://github.com/rust-lang/rust/pull/144689)
- [Error on invalid `#[should_panic]` attributes](https://github.com/rust-lang/rust/pull/143808)
- [Error on invalid `#[link]` attributes](https://github.com/rust-lang/rust/pull/143193)
- [Mark all deprecation lints in name resolution as deny-by-default and also report in dependencies](https://github.com/rust-lang/rust/pull/143929)
- The lint `semicolon_in_expressions_from_macros`, for `macro_rules!` macros in expression position that expand to end in a semicolon (`;`), is now deny-by-default. It was already warn-by-default, and a future compatibility warning (FCW) that warned even in dependencies. This lint will become a hard error in the future.
- [Trait impl modifiers (e.g., `unsafe`, `!`, `default`) in inherent impls are no longer syntactically valid](https://github.com/rust-lang/rust/pull/144386)
- [Start reporting future breakage for `ill_formed_attribute_input` in dependencies](https://github.com/rust-lang/rust/pull/144544)
- [Restrict the scope of temporaries created by the macros `pin!`, `format_args!`, `write!`, and `writeln!` in `if let` scrutinees in Rust Edition 2024.](https://github.com/rust-lang/rust/pull/145342) This applies [Rust Edition 2024's `if let` temporary scope rules](https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html) to these temporaries, which previously could live past the `if` expression regardless of Edition.
- [Invalid numeric literal suffixes in tuple indexing, tuple struct indexing, and struct field name positions are now correctly rejected](https://github.com/rust-lang/rust/pull/145463)
- [Closures marked with the keyword `static` are now syntactically invalid](https://github.com/rust-lang/rust/pull/145604)
- [Shebangs inside `--cfg` and `--check-cfg` arguments are no longer allowed](https://github.com/rust-lang/rust/pull/146211)
- [Add future incompatibility lint for temporary lifetime shortening in Rust 1.92](https://github.com/rust-lang/rust/pull/147056)
Cargo compatibility notes:
- `cargo publish` no longer keeps `.crate` tarballs as final build artifacts
when `build.build-dir` is set. These tarballs were previously included due to
an oversight and are now treated as intermediate artifacts.
To get `.crate` tarballs as final artifacts, use `cargo package`.
In a future version, this change will apply regardless of `build.build-dir`.
[#15910](https://github.com/rust-lang/cargo/pull/15910)
- Adjust Cargo messages to match rustc diagnostic style.
This changes some of the terminal colors used by Cargo messages.
[#15928](https://github.com/rust-lang/cargo/pull/15928)
- Tools and projects relying on the
[internal details of Cargo's `build-dir`](https://doc.rust-lang.org/cargo/reference/build-cache.html)
may not work for users changing their `build-dir` layout.
For those doing so, we'd recommend proactively testing these cases
particularly as we are considering changing the default location of the `build-dir` in the future
([cargo#16147](https://github.com/rust-lang/cargo/issues/16147)).
If you can't migrate off of Cargo's internal details,
we'd like to learn more about your use case as we prepare to change the layout of the `build-dir`
([cargo#15010](https://github.com/rust-lang/cargo/issues/15010)).
<a id="1.91.0-Internal-Changes"></a>
Internal Changes
----------------
These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.
- [Update to LLVM 21](https://github.com/rust-lang/rust/pull/143684)
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)
==========================
<a id="1.89.0-Language"></a>
Language
--------
- [Stabilize explicitly inferred const arguments (`feature(generic_arg_infer)`)](https://github.com/rust-lang/rust/pull/141610)
- [Add a warn-by-default `mismatched_lifetime_syntaxes` lint.](https://github.com/rust-lang/rust/pull/138677)
This lint detects when the same lifetime is referred to by different syntax categories between function arguments and return values, which can be confusing to read, especially in unsafe code.
This lint supersedes the warn-by-default `elided_named_lifetimes` lint.
- [Expand `unpredictable_function_pointer_comparisons` to also lint on function pointer comparisons in external macros](https://github.com/rust-lang/rust/pull/134536)
- [Make the `dangerous_implicit_autorefs` lint deny-by-default](https://github.com/rust-lang/rust/pull/141661)
- [Stabilize the avx512 target features](https://github.com/rust-lang/rust/pull/138940)
- [Stabilize `kl` and `widekl` target features for x86](https://github.com/rust-lang/rust/pull/140766)
- [Stabilize `sha512`, `sm3` and `sm4` target features for x86](https://github.com/rust-lang/rust/pull/140767)
- [Stabilize LoongArch target features `f`, `d`, `frecipe`, `lasx`, `lbt`, `lsx`, and `lvz`](https://github.com/rust-lang/rust/pull/135015)
- [Remove `i128` and `u128` from `improper_ctypes_definitions`](https://github.com/rust-lang/rust/pull/137306)
- [Stabilize `repr128` (`#[repr(u128)]`, `#[repr(i128)]`)](https://github.com/rust-lang/rust/pull/138285)
- [Allow `#![doc(test(attr(..)))]` everywhere](https://github.com/rust-lang/rust/pull/140560)
- [Extend temporary lifetime extension to also go through tuple struct and tuple variant constructors](https://github.com/rust-lang/rust/pull/140593)
- [`extern "C"` functions on the `wasm32-unknown-unknown` target now have a standards compliant ABI](https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/)
<a id="1.89.0-Compiler"></a>
Compiler
--------
- [Default to non-leaf frame pointers on aarch64-linux](https://github.com/rust-lang/rust/pull/140832)
- [Enable non-leaf frame pointers for Arm64EC Windows](https://github.com/rust-lang/rust/pull/140862)
- [Set Apple frame pointers by architecture](https://github.com/rust-lang/rust/pull/141797)
<a id="1.89.0-Platform-Support"></a>
Platform Support
----------------
- [Add new Tier-3 targets `loongarch32-unknown-none` and `loongarch32-unknown-none-softfloat`](https://github.com/rust-lang/rust/pull/142053)
- [`x86_64-apple-darwin` is in the process of being demoted to Tier 2 with host tools](https://github.com/rust-lang/rfcs/pull/3841)
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.89.0-Libraries"></a>
Libraries
---------
- [Specify the base path for `file!`](https://github.com/rust-lang/rust/pull/134442)
- [Allow storing `format_args!()` in a variable](https://github.com/rust-lang/rust/pull/140748)
- [Add `#[must_use]` to `[T; N]::map`](https://github.com/rust-lang/rust/pull/140957)
- [Implement `DerefMut` for `Lazy{Cell,Lock}`](https://github.com/rust-lang/rust/pull/129334)
- [Implement `Default` for `array::IntoIter`](https://github.com/rust-lang/rust/pull/141574)
- [Implement `Clone` for `slice::ChunkBy`](https://github.com/rust-lang/rust/pull/138016)
- [Implement `io::Seek` for `io::Take`](https://github.com/rust-lang/rust/pull/138023)
<a id="1.89.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`NonZero<char>`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html)
- Many intrinsics for x86, not enumerated here
- [AVX512 intrinsics](https://github.com/rust-lang/rust/issues/111137)
- [`SHA512`, `SM3` and `SM4` intrinsics](https://github.com/rust-lang/rust/issues/126624)
- [`File::lock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.lock)
- [`File::lock_shared`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.lock_shared)
- [`File::try_lock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_lock)
- [`File::try_lock_shared`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_lock_shared)
- [`File::unlock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.unlock)
- [`NonNull::from_ref`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.from_ref)
- [`NonNull::from_mut`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.from_mut)
- [`NonNull::without_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.without_provenance)
- [`NonNull::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.with_exposed_provenance)
- [`NonNull::expose_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.expose_provenance)
- [`OsString::leak`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.leak)
- [`PathBuf::leak`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.leak)
- [`Result::flatten`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.flatten)
- [`std::os::linux::net::TcpStreamExt::quickack`](https://doc.rust-lang.org/stable/std/os/linux/net/trait.TcpStreamExt.html#tymethod.quickack)
- [`std::os::linux::net::TcpStreamExt::set_quickack`](https://doc.rust-lang.org/stable/std/os/linux/net/trait.TcpStreamExt.html#tymethod.set_quickack)
These previously stable APIs are now stable in const contexts:
- [`<[T; N]>::as_mut_slice`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.as_mut_slice)
- [`<[u8]>::eq_ignore_ascii_case`](https://doc.rust-lang.org/stable/std/primitive.slice.html#impl-%5Bu8%5D/method.eq_ignore_ascii_case)
- [`str::eq_ignore_ascii_case`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-str/method.eq_ignore_ascii_case)
<a id="1.89.0-Cargo"></a>
Cargo
-----
- [`cargo fix` and `cargo clippy --fix` now default to the same Cargo target selection as other build commands.](https://github.com/rust-lang/cargo/pull/15192/) Previously it would apply to all targets (like binaries, examples, tests, etc.). The `--edition` flag still applies to all targets.
- [Stabilize doctest-xcompile.](https://github.com/rust-lang/cargo/pull/15462/) Doctests are now tested when cross-compiling. Just like other tests, it will use the [`runner` setting](https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner) to run the tests. If you need to disable tests for a target, you can use the [ignore doctest attribute](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#ignoring-targets) to specify the targets to ignore.
<a id="1.89.0-Rustdoc"></a>
Rustdoc
-----
- [On mobile, make the sidebar full width and linewrap](https://github.com/rust-lang/rust/pull/139831). This makes long section and item names much easier to deal with on mobile.
<a id="1.89.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Make `missing_fragment_specifier` an unconditional error](https://github.com/rust-lang/rust/pull/128425)
- [Enabling the `neon` target feature on `aarch64-unknown-none-softfloat` causes a warning](https://github.com/rust-lang/rust/pull/135160) because mixing code with and without that target feature is not properly supported by LLVM
- [Sized Hierarchy: Part I](https://github.com/rust-lang/rust/pull/137944)
- Introduces a small breaking change affecting `?Sized` bounds on impls on recursive types which contain associated type projections. It is not expected to affect any existing published crates. Can be fixed by refactoring the involved types or opting into the `sized_hierarchy` unstable feature. See the [FCP report](https://github.com/rust-lang/rust/pull/137944#issuecomment-2912207485) for a code example.
- The warn-by-default `elided_named_lifetimes` lint is [superseded by the warn-by-default `mismatched_lifetime_syntaxes` lint.](https://github.com/rust-lang/rust/pull/138677)
- [Error on recursive opaque types earlier in the type checker](https://github.com/rust-lang/rust/pull/139419)
- [Type inference side effects from requiring element types of array repeat expressions are `Copy` are now only available at the end of type checking](https://github.com/rust-lang/rust/pull/139635)
- [The deprecated accidentally-stable `std::intrinsics::{copy,copy_nonoverlapping,write_bytes}` are now proper intrinsics](https://github.com/rust-lang/rust/pull/139916). There are no debug assertions guarding against UB, and they cannot be coerced to function pointers.
- [Remove long-deprecated `std::intrinsics::drop_in_place`](https://github.com/rust-lang/rust/pull/140151)
- [Make well-formedness predicates no longer coinductive](https://github.com/rust-lang/rust/pull/140208)
- [Remove hack when checking impl method compatibility](https://github.com/rust-lang/rust/pull/140557)
- [Remove unnecessary type inference due to built-in trait object impls](https://github.com/rust-lang/rust/pull/141352)
- [Lint against "stdcall", "fastcall", and "cdecl" on non-x86-32 targets](https://github.com/rust-lang/rust/pull/141435)
- [Future incompatibility warnings relating to the never type (`!`) are now reported in dependencies](https://github.com/rust-lang/rust/pull/141937)
- [Ensure `std::ptr::copy_*` intrinsics also perform the static self-init checks](https://github.com/rust-lang/rust/pull/142575)
- [`extern "C"` functions on the `wasm32-unknown-unknown` target now have a standards compliant ABI](https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/)
<a id="1.89.0-Internal-Changes"></a>
Internal Changes
----------------
These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.
- [Correctly un-remap compiler sources paths with the `rustc-dev` component](https://github.com/rust-lang/rust/pull/142377)
Version 1.88.0 (2025-06-26)
==========================
@ -1641,7 +2115,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>

View file

@ -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`.
@ -470,9 +476,6 @@
# when the stage 0 compiler is actually built from in-tree sources.
#build.compiletest-allow-stage0 = false
# Whether to use the precompiled stage0 libtest with compiletest.
#build.compiletest-use-stage0-libtest = true
# Default value for the `--extra-checks` flag of tidy.
#
# See `./x test tidy --help` for details.
@ -482,7 +485,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).
@ -740,22 +743,25 @@
# result (broken, compiling, testing) into this JSON file.
#rust.save-toolstates = <none> (path)
# This is an array of the codegen backends that will be compiled for the rustc
# that's being compiled. The default is to only build the LLVM codegen backend,
# and currently the only standard options supported are `"llvm"`, `"cranelift"`
# and `"gcc"`. The first backend in this list will be used as default by rustc
# when no explicit backend is specified.
# This array serves three distinct purposes:
# - Backends in this list will be automatically compiled and included in the sysroot of each
# rustc compiled by bootstrap.
# - The first backend in this list will be configured as the **default codegen backend** by each
# rustc compiled by bootstrap. In other words, if the first backend is e.g. cranelift, then when
# we build a stage 1 rustc, it will by default compile Rust programs using the Cranelift backend.
# This also means that stage 2 rustc would get built by the Cranelift backend.
# - Running `x dist` (without additional arguments, or with `--include-default-paths`) will produce
# a dist component/tarball for the Cranelift backend if it is included in this array.
#
# Note that the LLVM codegen backend is special and will always be built and distributed.
#
# Currently, the only standard options supported here are `"llvm"`, `"cranelift"` and `"gcc"`.
#rust.codegen-backends = ["llvm"]
# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and
# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be
# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from
# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will
# make this default to false.
#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute,
#rust.lld = false, except for targets that opt into LLD (see `target.default-linker-linux-override`)
# Indicates whether LLD will be used to link Rust crates during bootstrap on
# supported platforms.
# Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD.
# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH
# will be used.
# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used.
@ -763,7 +769,7 @@
# On MSVC, LLD will not be used if we're cross linking.
#
# Explicitly setting the linker for a target will override this option when targeting MSVC.
#rust.use-lld = false
#rust.bootstrap-override-lld = false
# Indicates whether some LLVM tools, like llvm-objdump, will be made available in the
# sysroot.
@ -845,6 +851,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
#
@ -925,7 +942,7 @@
# Linker to be used to bootstrap Rust code. Note that the
# default value is platform specific, and if not specified it may also depend on
# what platform is crossing to what platform.
# Setting this will override the `use-lld` option for Rust code when targeting MSVC.
# Setting this will override the `bootstrap-override-lld` option for Rust code when targeting MSVC.
#linker = "cc" (path)
# Should rustc and the standard library be built with split debuginfo? Default
@ -1033,14 +1050,30 @@
#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.
#jemalloc = rust.jemalloc (bool)
# The linker configuration that will *override* the default linker used for Linux
# targets in the built compiler.
#
# The following values are supported:
# - `off` => do not apply any override and use the default linker. This can be used to opt out of
# linker overrides set by bootstrap for specific targets (see below).
# - `self-contained-lld-cc` => override the default linker to be self-contained LLD (`rust-lld`)
# that is invoked through `cc`.
#
# Currently, the following targets automatically opt into the self-contained LLD linker, unless you
# pass `off`:
# - x86_64-unknown-linux-gnu
#default-linker-linux-override = "off" (for most targets)

View file

@ -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

View file

@ -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();

View file

@ -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",

View file

@ -42,22 +42,22 @@ impl Reg {
let dl = cx.data_layout();
match self.kind {
RegKind::Integer => match self.size.bits() {
1 => dl.i1_align.abi,
2..=8 => dl.i8_align.abi,
9..=16 => dl.i16_align.abi,
17..=32 => dl.i32_align.abi,
33..=64 => dl.i64_align.abi,
65..=128 => dl.i128_align.abi,
1 => dl.i1_align,
2..=8 => dl.i8_align,
9..=16 => dl.i16_align,
17..=32 => dl.i32_align,
33..=64 => dl.i64_align,
65..=128 => dl.i128_align,
_ => panic!("unsupported integer: {self:?}"),
},
RegKind::Float => match self.size.bits() {
16 => dl.f16_align.abi,
32 => dl.f32_align.abi,
64 => dl.f64_align.abi,
128 => dl.f128_align.abi,
16 => dl.f16_align,
32 => dl.f32_align,
64 => dl.f64_align,
128 => dl.f128_align,
_ => panic!("unsupported float: {self:?}"),
},
RegKind::Vector => dl.llvmlike_vector_align(self.size).abi,
RegKind::Vector => dl.llvmlike_vector_align(self.size),
}
}
}

View file

@ -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,
}
}
}

View file

@ -174,11 +174,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// Non-power-of-two vectors have padding up to the next power-of-two.
// If we're a packed repr, remove the padding while keeping the alignment as close
// to a vector as possible.
(BackendRepr::Memory { sized: true }, AbiAlign { abi: Align::max_aligned_factor(size) })
(BackendRepr::Memory { sized: true }, Align::max_aligned_factor(size))
} else {
(BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
};
let size = size.align_to(align.abi);
let size = size.align_to(align);
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
@ -190,7 +190,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche: elt.largest_niche,
uninhabited: false,
size,
align,
align: AbiAlign::new(align),
max_repr_align: None,
unadjusted_abi_align: elt.align.abi,
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
@ -388,7 +388,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
return Err(LayoutCalculatorError::UnexpectedUnsized(*field));
}
align = align.max(field.align);
align = align.max(field.align.abi);
max_repr_align = max_repr_align.max(field.max_repr_align);
size = cmp::max(size, field.size);
@ -423,13 +423,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
}
if let Some(pack) = repr.pack {
align = align.min(AbiAlign::new(pack));
align = align.min(pack);
}
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
// See documentation on `LayoutData::unadjusted_abi_align`.
let unadjusted_abi_align = align.abi;
let unadjusted_abi_align = align;
if let Some(repr_align) = repr.align {
align = align.max(AbiAlign::new(repr_align));
align = align.max(repr_align);
}
// `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate.
let align = align;
@ -441,14 +441,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Ok(Some((repr, _))) => match repr {
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _)
if repr.scalar_align(dl).unwrap() != align.abi =>
if repr.scalar_align(dl).unwrap() != align =>
{
BackendRepr::Memory { sized: true }
}
// Vectors require at least element alignment, else disable the opt
BackendRepr::SimdVector { element, count: _ }
if element.align(dl).abi > align.abi =>
{
BackendRepr::SimdVector { element, count: _ } if element.align(dl).abi > align => {
BackendRepr::Memory { sized: true }
}
// the alignment tests passed and we can use this
@ -474,8 +472,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
backend_repr,
largest_niche: None,
uninhabited: false,
align,
size: size.align_to(align.abi),
align: AbiAlign::new(align),
size: size.align_to(align),
max_repr_align,
unadjusted_abi_align,
randomization_seed: combined_seed,
@ -594,23 +592,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;
}
@ -621,7 +609,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let mut align = dl.aggregate_align;
let mut max_repr_align = repr.align;
let mut unadjusted_abi_align = align.abi;
let mut unadjusted_abi_align = align;
let mut variant_layouts = variants
.iter_enumerated()
@ -629,7 +617,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
st.variants = Variants::Single { index: j };
align = align.max(st.align);
align = align.max(st.align.abi);
max_repr_align = max_repr_align.max(st.max_repr_align);
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
@ -656,7 +644,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let (niche_start, niche_scalar) = niche.reserve(dl, count)?;
let niche_offset = niche.offset;
let niche_size = niche.value.size(dl);
let size = variant_layouts[largest_variant_index].size.align_to(align.abi);
let size = variant_layouts[largest_variant_index].size.align_to(align);
let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
if i == largest_variant_index {
@ -709,7 +697,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
.iter_enumerated()
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
let same_size = size == variant_layouts[largest_variant_index].size;
let same_align = align == variant_layouts[largest_variant_index].align;
let same_align = align == variant_layouts[largest_variant_index].align.abi;
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
let abi = if same_size && same_align && others_zst {
@ -746,7 +734,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(),
@ -756,13 +744,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche,
uninhabited,
size,
align,
align: AbiAlign::new(align),
max_repr_align,
unadjusted_abi_align,
randomization_seed: combined_seed,
};
Some(TmpLayout { layout, variants: variant_layouts })
Some(layout)
};
let niche_filling_layout = calculate_niche_filling_layout();
@ -828,7 +816,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let mut align = dl.aggregate_align;
let mut max_repr_align = repr.align;
let mut unadjusted_abi_align = align.abi;
let mut unadjusted_abi_align = align;
let mut size = Size::ZERO;
@ -870,7 +858,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
}
}
size = cmp::max(size, st.size);
align = align.max(st.align);
align = align.max(st.align.abi);
max_repr_align = max_repr_align.max(st.max_repr_align);
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
Ok(st)
@ -878,7 +866,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
// Align the maximum variant size to the largest alignment.
size = size.align_to(align.abi);
size = size.align_to(align);
// FIXME(oli-obk): deduplicate and harden these checks
if size.bytes() >= dl.obj_size_bound() {
@ -1052,7 +1040,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
};
if pair_offsets[FieldIdx::new(0)] == Size::ZERO
&& pair_offsets[FieldIdx::new(1)] == *offset
&& align == pair.align
&& align == pair.align.abi
&& size == pair.size
{
// We can use `ScalarPair` only when it matches our
@ -1076,7 +1064,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// Also need to bump up the size and alignment, so that the entire value fits
// in here.
variant.size = cmp::max(variant.size, size);
variant.align.abi = cmp::max(variant.align.abi, align.abi);
variant.align.abi = cmp::max(variant.align.abi, align);
}
}
}
@ -1093,7 +1081,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(),
@ -1102,25 +1090,23 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche,
uninhabited,
backend_repr: abi,
align,
align: AbiAlign::new(align),
size,
max_repr_align,
unadjusted_abi_align,
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 +1115,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<
@ -1190,7 +1167,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// To allow unsizing `&Foo<Type>` -> `&Foo<dyn Trait>`, the layout of the struct must
// not depend on the layout of the tail.
let max_field_align =
fields_excluding_tail.iter().map(|f| f.align.abi.bytes()).max().unwrap_or(1);
fields_excluding_tail.iter().map(|f| f.align.bytes()).max().unwrap_or(1);
let largest_niche_size = fields_excluding_tail
.iter()
.filter_map(|f| f.largest_niche)
@ -1210,7 +1187,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} else {
// Returns `log2(effective-align)`. The calculation assumes that size is an
// integer multiple of align, except for ZSTs.
let align = layout.align.abi.bytes();
let align = layout.align.bytes();
let size = layout.size.bytes();
let niche_size = layout.largest_niche.map(|n| n.available(dl)).unwrap_or(0);
// Group [u8; 4] with align-4 or [u8; 6] with align-2 fields.
@ -1309,7 +1286,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
let prefix_align =
if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align };
align = align.max(AbiAlign::new(prefix_align));
align = align.max(prefix_align);
offset = prefix_size.align_to(prefix_align);
}
for &i in &inverse_memory_index {
@ -1333,7 +1310,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
field.align
};
offset = offset.align_to(field_align.abi);
align = align.max(field_align);
align = align.max(field_align.abi);
max_repr_align = max_repr_align.max(field.max_repr_align);
debug!("univariant offset: {:?} field: {:#?}", offset, field);
@ -1360,9 +1337,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
// See documentation on `LayoutData::unadjusted_abi_align`.
let unadjusted_abi_align = align.abi;
let unadjusted_abi_align = align;
if let Some(repr_align) = repr.align {
align = align.max(AbiAlign::new(repr_align));
align = align.max(repr_align);
}
// `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate.
let align = align;
@ -1381,7 +1358,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices()));
inverse_memory_index.into_iter().map(|it| it.index() as u32).collect()
};
let size = min_size.align_to(align.abi);
let size = min_size.align_to(align);
// FIXME(oli-obk): deduplicate and harden these checks
if size.bytes() >= dl.obj_size_bound() {
return Err(LayoutCalculatorError::SizeOverflow);
@ -1404,8 +1381,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
layout_of_single_non_zst_field = Some(field);
// Field fills the struct and it has a scalar or scalar pair ABI.
if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size
{
if offsets[i].bytes() == 0 && align == field.align.abi && size == field.size {
match field.backend_repr {
// For plain scalars, or vectors of them, we can't unpack
// newtypes for `#[repr(C)]`, as that affects C ABIs.
@ -1449,7 +1425,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
};
if offsets[i] == pair_offsets[FieldIdx::new(0)]
&& offsets[j] == pair_offsets[FieldIdx::new(1)]
&& align == pair.align
&& align == pair.align.abi
&& size == pair.size
{
// We can use `ScalarPair` only when it matches our
@ -1471,7 +1447,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Some(l) => l.unadjusted_abi_align,
None => {
// `repr(transparent)` with all ZST fields.
align.abi
align
}
}
} else {
@ -1486,7 +1462,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
backend_repr: abi,
largest_niche,
uninhabited,
align,
align: AbiAlign::new(align),
size,
max_repr_align,
unadjusted_abi_align,
@ -1509,7 +1485,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
for i in layout.fields.index_by_increasing_offset() {
let offset = layout.fields.offset(i);
let f = &fields[FieldIdx::new(i)];
write!(s, "[o{}a{}s{}", offset.bytes(), f.align.abi.bytes(), f.size.bytes()).unwrap();
write!(s, "[o{}a{}s{}", offset.bytes(), f.align.bytes(), f.size.bytes()).unwrap();
if let Some(n) = f.largest_niche {
write!(
s,

View file

@ -4,7 +4,8 @@ use rustc_hashes::Hash64;
use rustc_index::{Idx, IndexVec};
use crate::{
BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants,
AbiAlign, BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size,
Variants,
};
/// "Simple" layout constructors that cannot fail.
@ -20,10 +21,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
backend_repr: BackendRepr::Memory { sized },
largest_niche: None,
uninhabited: false,
align: dl.i8_align,
align: AbiAlign::new(dl.i8_align),
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
unadjusted_abi_align: dl.i8_align,
randomization_seed: Hash64::new(0),
}
}
@ -37,10 +38,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
backend_repr: BackendRepr::Memory { sized: true },
largest_niche: None,
uninhabited: true,
align: dl.i8_align,
align: AbiAlign::new(dl.i8_align),
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
unadjusted_abi_align: dl.i8_align,
randomization_seed: Hash64::ZERO,
}
}
@ -89,10 +90,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
let dl = cx.data_layout();
let b_align = b.align(dl);
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
let b_offset = a.size(dl).align_to(b_align.abi);
let size = (b_offset + b.size(dl)).align_to(align.abi);
let b_align = b.align(dl).abi;
let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align);
let b_offset = a.size(dl).align_to(b_align);
let size = (b_offset + b.size(dl)).align_to(align);
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
// returns the last maximum.
@ -112,10 +113,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
backend_repr: BackendRepr::ScalarPair(a, b),
largest_niche,
uninhabited: false,
align,
align: AbiAlign::new(align),
size,
max_repr_align: None,
unadjusted_abi_align: align.abi,
unadjusted_abi_align: align,
randomization_seed: Hash64::new(combined_seed),
}
}
@ -138,10 +139,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
backend_repr: BackendRepr::Memory { sized: true },
largest_niche: None,
uninhabited: true,
align: dl.i8_align,
align: AbiAlign::new(dl.i8_align),
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
unadjusted_abi_align: dl.i8_align,
randomization_seed: Hash64::ZERO,
}
}

View file

@ -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};
@ -227,7 +229,7 @@ pub struct PointerSpec {
/// The size of the bitwise representation of the pointer.
pointer_size: Size,
/// The alignment of pointers for this address space
pointer_align: AbiAlign,
pointer_align: Align,
/// The size of the value a pointer can be offset by in this address space.
pointer_offset: Size,
/// Pointers into this address space contain extra metadata
@ -240,20 +242,20 @@ pub struct PointerSpec {
#[derive(Debug, PartialEq, Eq)]
pub struct TargetDataLayout {
pub endian: Endian,
pub i1_align: AbiAlign,
pub i8_align: AbiAlign,
pub i16_align: AbiAlign,
pub i32_align: AbiAlign,
pub i64_align: AbiAlign,
pub i128_align: AbiAlign,
pub f16_align: AbiAlign,
pub f32_align: AbiAlign,
pub f64_align: AbiAlign,
pub f128_align: AbiAlign,
pub aggregate_align: AbiAlign,
pub i1_align: Align,
pub i8_align: Align,
pub i16_align: Align,
pub i32_align: Align,
pub i64_align: Align,
pub i128_align: Align,
pub f16_align: Align,
pub f32_align: Align,
pub f64_align: Align,
pub f128_align: Align,
pub aggregate_align: Align,
/// Alignments for vector types.
pub vector_align: Vec<(Size, AbiAlign)>,
pub vector_align: Vec<(Size, Align)>,
pub default_address_space: AddressSpace,
pub default_address_space_pointer_spec: PointerSpec,
@ -280,25 +282,25 @@ impl Default for TargetDataLayout {
let align = |bits| Align::from_bits(bits).unwrap();
TargetDataLayout {
endian: Endian::Big,
i1_align: AbiAlign::new(align(8)),
i8_align: AbiAlign::new(align(8)),
i16_align: AbiAlign::new(align(16)),
i32_align: AbiAlign::new(align(32)),
i64_align: AbiAlign::new(align(32)),
i128_align: AbiAlign::new(align(32)),
f16_align: AbiAlign::new(align(16)),
f32_align: AbiAlign::new(align(32)),
f64_align: AbiAlign::new(align(64)),
f128_align: AbiAlign::new(align(128)),
aggregate_align: AbiAlign { abi: align(8) },
i1_align: align(8),
i8_align: align(8),
i16_align: align(16),
i32_align: align(32),
i64_align: align(32),
i128_align: align(32),
f16_align: align(16),
f32_align: align(32),
f64_align: align(64),
f128_align: align(128),
aggregate_align: align(8),
vector_align: vec![
(Size::from_bits(64), AbiAlign::new(align(64))),
(Size::from_bits(128), AbiAlign::new(align(128))),
(Size::from_bits(64), align(64)),
(Size::from_bits(128), align(128)),
],
default_address_space: AddressSpace::ZERO,
default_address_space_pointer_spec: PointerSpec {
pointer_size: Size::from_bits(64),
pointer_align: AbiAlign::new(align(64)),
pointer_align: align(64),
pointer_offset: Size::from_bits(64),
_is_fat: false,
},
@ -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 },
}
@ -358,7 +360,7 @@ impl TargetDataLayout {
.map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err })
};
let abi = parse_bits(s, "alignment", cause)?;
Ok(AbiAlign::new(align_from_bits(abi)?))
Ok(align_from_bits(abi)?)
};
// Parse an alignment sequence, possibly in the form `<align>[:<preferred_alignment>]`,
@ -594,7 +596,7 @@ impl TargetDataLayout {
/// psABI-mandated alignment for a vector type, if any
#[inline]
fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAlign> {
fn cabi_vector_align(&self, vec_size: Size) -> Option<Align> {
self.vector_align
.iter()
.find(|(size, _align)| *size == vec_size)
@ -603,10 +605,9 @@ impl TargetDataLayout {
/// an alignment resembling the one LLVM would pick for a vector
#[inline]
pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAlign {
self.cabi_vector_align(vec_size).unwrap_or(AbiAlign::new(
Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(),
))
pub fn llvmlike_vector_align(&self, vec_size: Size) -> Align {
self.cabi_vector_align(vec_size)
.unwrap_or(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
}
/// Get the pointer size in the default data address space.
@ -652,21 +653,19 @@ impl TargetDataLayout {
/// Get the pointer alignment in the default data address space.
#[inline]
pub fn pointer_align(&self) -> AbiAlign {
self.default_address_space_pointer_spec.pointer_align
AbiAlign::new(self.default_address_space_pointer_spec.pointer_align)
}
/// Get the pointer alignment in a specific address space.
#[inline]
pub fn pointer_align_in(&self, c: AddressSpace) -> AbiAlign {
if c == self.default_address_space {
return self.default_address_space_pointer_spec.pointer_align;
}
if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) {
AbiAlign::new(if c == self.default_address_space {
self.default_address_space_pointer_spec.pointer_align
} else if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) {
e.1.pointer_align
} else {
panic!("Use of unknown address space {c:?}");
}
})
}
}
@ -1183,13 +1182,13 @@ impl Integer {
use Integer::*;
let dl = cx.data_layout();
match self {
AbiAlign::new(match self {
I8 => dl.i8_align,
I16 => dl.i16_align,
I32 => dl.i32_align,
I64 => dl.i64_align,
I128 => dl.i128_align,
}
})
}
/// Returns the largest signed value that can be represented by this Integer.
@ -1309,12 +1308,12 @@ impl Float {
use Float::*;
let dl = cx.data_layout();
match self {
AbiAlign::new(match self {
F16 => dl.f16_align,
F32 => dl.f32_align,
F64 => dl.f64_align,
F128 => dl.f128_align,
}
})
}
}
@ -2157,7 +2156,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
/// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
pub fn is_1zst(&self) -> bool {
self.is_sized() && self.size.bytes() == 0 && self.align.abi.bytes() == 1
self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1
}
/// Returns `true` if the type is a ZST and not unsized.

View file

@ -19,8 +19,8 @@ impl<T> TypedArena<T> {
unsafe {
// Clear the last chunk, which is partially filled.
let mut chunks_borrow = self.chunks.borrow_mut();
if let Some(mut last_chunk) = chunks_borrow.last_mut() {
self.clear_last_chunk(&mut last_chunk);
if let Some(last_chunk) = chunks_borrow.last_mut() {
self.clear_last_chunk(last_chunk);
let len = chunks_borrow.len();
// If `T` is ZST, code below has no effect.
for mut chunk in chunks_borrow.drain(..len - 1) {

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@
use std::fmt;
use std::marker::PhantomData;
use crate::ptr::P;
use crate::tokenstream::LazyAttrTokenStream;
use crate::{
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
@ -53,7 +52,7 @@ impl_has_node_id!(
WherePredicate,
);
impl<T: HasNodeId> HasNodeId for P<T> {
impl<T: HasNodeId> HasNodeId for Box<T> {
fn node_id(&self) -> NodeId {
(**self).node_id()
}
@ -119,7 +118,7 @@ impl<T: HasTokens> HasTokens for Option<T> {
}
}
impl<T: HasTokens> HasTokens for P<T> {
impl<T: HasTokens> HasTokens for Box<T> {
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
(**self).tokens()
}
@ -245,7 +244,7 @@ impl_has_attrs!(
);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
impl<T: HasAttrs> HasAttrs for P<T> {
impl<T: HasAttrs> HasAttrs for Box<T> {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
fn attrs(&self) -> &[Attribute] {
(**self).attrs()
@ -322,8 +321,8 @@ impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
}
// FIXME: remove after `stmt_expr_attributes` is stabilized.
impl<T, Tag> From<AstNodeWrapper<P<T>, Tag>> for AstNodeWrapper<T, Tag> {
fn from(value: AstNodeWrapper<P<T>, Tag>) -> Self {
impl<T, Tag> From<AstNodeWrapper<Box<T>, Tag>> for AstNodeWrapper<T, Tag> {
fn from(value: AstNodeWrapper<Box<T>, Tag>) -> Self {
AstNodeWrapper { wrapped: *value.wrapped, tag: value.tag }
}
}

View file

@ -13,7 +13,6 @@ use crate::ast::{
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
PathSegment, Safety,
};
use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
use crate::tokenstream::{
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
@ -87,10 +86,10 @@ impl AttributeExt for Attribute {
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
fn is_doc_comment(&self) -> bool {
fn is_doc_comment(&self) -> Option<Span> {
match self.kind {
AttrKind::Normal(..) => false,
AttrKind::DocComment(..) => true,
AttrKind::Normal(..) => None,
AttrKind::DocComment(..) => Some(self.span),
}
}
@ -660,7 +659,7 @@ pub fn mk_attr_from_item(
span: Span,
) -> Attribute {
Attribute {
kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),
id: g.mk_attr_id(),
style,
span,
@ -710,7 +709,7 @@ pub fn mk_attr_name_value_str(
span: Span,
) -> Attribute {
let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
let expr = P(Expr {
let expr = Box::new(Expr {
id: DUMMY_NODE_ID,
kind: ExprKind::Lit(lit),
span,
@ -777,7 +776,7 @@ pub trait AttributeExt: Debug {
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
fn is_doc_comment(&self) -> bool;
fn is_doc_comment(&self) -> Option<Span>;
#[inline]
fn has_name(&self, name: Symbol) -> bool {
@ -864,8 +863,9 @@ impl Attribute {
AttributeExt::path_matches(self, name)
}
// on ast attributes we return a bool since that's what most code already expects
pub fn is_doc_comment(&self) -> bool {
AttributeExt::is_doc_comment(self)
AttributeExt::is_doc_comment(self).is_some()
}
#[inline]

View file

@ -1,7 +1,5 @@
use rustc_span::{Symbol, sym};
use crate::attr::{self, AttributeExt};
#[derive(Debug)]
pub enum EntryPointType {
/// This function is not an entrypoint.
@ -30,11 +28,11 @@ pub enum EntryPointType {
}
pub fn entry_point_type(
attrs: &[impl AttributeExt],
has_rustc_main: bool,
at_root: bool,
name: Option<Symbol>,
) -> EntryPointType {
if attr::contains_name(attrs, sym::rustc_main) {
if has_rustc_main {
EntryPointType::RustcMainAttr
} else if let Some(name) = name
&& name == sym::main

View file

@ -3,7 +3,9 @@ use rustc_span::{Symbol, sym};
#[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)]
pub enum AllocatorKind {
/// Use `#[global_allocator]` as global allocator.
Global,
/// Use the default implementation in libstd as global allocator.
Default,
}
@ -15,25 +17,37 @@ pub fn default_fn_name(base: Symbol) -> String {
format!("__rdl_{base}")
}
pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str {
match alloc_error_handler_kind {
AllocatorKind::Global => "__rg_oom",
AllocatorKind::Default => "__rdl_oom",
}
}
pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler;
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2";
/// Argument or return type for methods in the allocator shim
#[derive(Copy, Clone)]
pub enum AllocatorTy {
Layout,
Never,
Ptr,
ResultPtr,
Unit,
Usize,
}
/// Some allocator methods are known to the compiler: they act more like
/// intrinsics/language primitives than library-defined functions.
/// FIXME: ideally this would be derived from attributes like `#[rustc_allocator]`,
/// so we don't have two sources of truth.
#[derive(Copy, Clone, Debug)]
pub enum SpecialAllocatorMethod {
Alloc,
AllocZeroed,
Dealloc,
Realloc,
}
/// A method that will be codegened in the allocator shim.
#[derive(Copy, Clone)]
pub struct AllocatorMethod {
pub name: Symbol,
pub special: Option<SpecialAllocatorMethod>,
pub inputs: &'static [AllocatorMethodInput],
pub output: AllocatorTy,
}
@ -46,11 +60,13 @@ pub struct AllocatorMethodInput {
pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
AllocatorMethod {
name: sym::alloc,
special: Some(SpecialAllocatorMethod::Alloc),
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
output: AllocatorTy::ResultPtr,
},
AllocatorMethod {
name: sym::dealloc,
special: Some(SpecialAllocatorMethod::Dealloc),
inputs: &[
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@ -59,6 +75,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
},
AllocatorMethod {
name: sym::realloc,
special: Some(SpecialAllocatorMethod::Realloc),
inputs: &[
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@ -68,6 +85,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
},
AllocatorMethod {
name: sym::alloc_zeroed,
special: Some(SpecialAllocatorMethod::AllocZeroed),
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
output: AllocatorTy::ResultPtr,
},

View file

@ -6,8 +6,8 @@
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use crate::expand::typetree::TypeTree;
use crate::expand::{Decodable, Encodable, HashStable_Generic};
use crate::ptr::P;
use crate::{Ty, TyKind};
/// Forward and Reverse Mode are well known names for automatic differentiation implementations.
@ -85,6 +85,8 @@ pub struct AutoDiffItem {
/// The name of the function being generated
pub target: String,
pub attrs: AutoDiffAttrs,
pub inputs: Vec<TypeTree>,
pub output: TypeTree,
}
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@ -162,7 +164,7 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
/// since Duplicated expects a mutable ref/ptr and we would thus end up with a shadow value
/// who is an indirect type, which doesn't match the primal scalar type. We can't prevent
/// users here from marking scalars as Duplicated, due to type aliases.
pub fn valid_ty_for_activity(ty: &P<Ty>, activity: DiffActivity) -> bool {
pub fn valid_ty_for_activity(ty: &Box<Ty>, activity: DiffActivity) -> bool {
use DiffActivity::*;
// It's always allowed to mark something as Const, since we won't compute derivatives wrt. it.
// Dual variants also support all types.
@ -276,14 +278,22 @@ impl AutoDiffAttrs {
!matches!(self.mode, DiffMode::Error | DiffMode::Source)
}
pub fn into_item(self, source: String, target: String) -> AutoDiffItem {
AutoDiffItem { source, target, attrs: self }
pub fn into_item(
self,
source: String,
target: String,
inputs: Vec<TypeTree>,
output: TypeTree,
) -> AutoDiffItem {
AutoDiffItem { source, target, inputs, output, attrs: self }
}
}
impl fmt::Display for AutoDiffItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Differentiating {} -> {}", self.source, self.target)?;
write!(f, " with attributes: {:?}", self.attrs)
write!(f, " with attributes: {:?}", self.attrs)?;
write!(f, " with inputs: {:?}", self.inputs)?;
write!(f, " with output: {:?}", self.output)
}
}

View file

@ -31,6 +31,7 @@ pub enum Kind {
Half,
Float,
Double,
F128,
Unknown,
}

View file

@ -3,7 +3,6 @@ use rustc_macros::{Decodable, Encodable, Walkable};
use rustc_span::{Ident, Span, Symbol};
use crate::Expr;
use crate::ptr::P;
use crate::token::LitKind;
// Definitions:
@ -147,7 +146,7 @@ impl FormatArguments {
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct FormatArgument {
pub kind: FormatArgumentKind,
pub expr: P<Expr>,
pub expr: Box<Expr>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]

View file

@ -15,6 +15,7 @@
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_order_by)]
#![feature(macro_metavar_expr)]
#![feature(rustdoc_internals)]
#![recursion_limit = "256"]
@ -37,7 +38,6 @@ pub mod expand;
pub mod format;
pub mod mut_visit;
pub mod node_id;
pub mod ptr;
pub mod token;
pub mod tokenstream;
pub mod visit;

View file

@ -17,7 +17,6 @@ use smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec;
use crate::ast::*;
use crate::ptr::P;
use crate::tokenstream::*;
use crate::visit::{AssocCtxt, BoundKind, FnCtxt, LifetimeCtxt, VisitorResult, try_visit};
@ -41,7 +40,7 @@ pub(crate) trait MutVisitable<V: MutVisitor> {
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra);
}
impl<V: MutVisitor, T: ?Sized> MutVisitable<V> for P<T>
impl<V: MutVisitor, T: ?Sized> MutVisitable<V> for Box<T>
where
T: MutVisitable<V>,
{
@ -293,15 +292,15 @@ macro_rules! generate_flat_map_visitor_fns {
}
generate_flat_map_visitor_fns! {
visit_items, P<Item>, flat_map_item;
visit_foreign_items, P<ForeignItem>, flat_map_foreign_item;
visit_items, Box<Item>, flat_map_item;
visit_foreign_items, Box<ForeignItem>, flat_map_foreign_item;
visit_generic_params, GenericParam, flat_map_generic_param;
visit_stmts, Stmt, flat_map_stmt;
visit_exprs, P<Expr>, filter_map_expr;
visit_exprs, Box<Expr>, filter_map_expr;
visit_expr_fields, ExprField, flat_map_expr_field;
visit_pat_fields, PatField, flat_map_pat_field;
visit_variants, Variant, flat_map_variant;
visit_assoc_items, P<AssocItem>, flat_map_assoc_item, ctxt: AssocCtxt;
visit_assoc_items, Box<AssocItem>, flat_map_assoc_item, ctxt: AssocCtxt;
visit_where_predicates, WherePredicate, flat_map_where_predicate;
visit_params, Param, flat_map_param;
visit_field_defs, FieldDef, flat_map_field_def;
@ -333,12 +332,12 @@ generate_walk_flat_map_fns! {
walk_flat_map_where_predicate(WherePredicate) => visit_where_predicate;
walk_flat_map_field_def(FieldDef) => visit_field_def;
walk_flat_map_expr_field(ExprField) => visit_expr_field;
walk_flat_map_item(P<Item>) => visit_item;
walk_flat_map_foreign_item(P<ForeignItem>) => visit_foreign_item;
walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
walk_flat_map_item(Box<Item>) => visit_item;
walk_flat_map_foreign_item(Box<ForeignItem>) => visit_foreign_item;
walk_flat_map_assoc_item(Box<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
}
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: Box<Expr>) -> Option<Box<Expr>> {
vis.visit_expr(&mut e);
Some(e)
}

View file

@ -1,11 +0,0 @@
/// A pointer type that uniquely owns a heap allocation of type T.
///
/// This used to be its own type, but now it's just a typedef for `Box` and we are planning to
/// remove it soon.
pub type P<T> = Box<T>;
/// Construct a `P<T>` from a `T` value.
#[allow(non_snake_case)]
pub fn P<T>(value: T) -> P<T> {
Box::new(value)
}

View file

@ -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 }
}
}
@ -885,11 +881,11 @@ impl Token {
}
pub fn is_qpath_start(&self) -> bool {
self == &Lt || self == &Shl
matches!(self.kind, Lt | Shl)
}
pub fn is_path_start(&self) -> bool {
self == &PathSep
self.kind == PathSep
|| self.is_qpath_start()
|| matches!(self.is_metavar_seq(), Some(MetaVarKind::Path))
|| self.is_path_segment_keyword()

View file

@ -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;
@ -57,9 +48,7 @@ impl TokenTree {
match (self, other) {
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
(TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => {
delim == delim2
&& tts.len() == tts2.len()
&& tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b))
delim == delim2 && tts.iter().eq_by(tts2.iter(), |a, b| a.eq_unspanned(b))
}
_ => false,
}
@ -99,17 +88,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 +534,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 +594,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 +772,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 +894,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

View file

@ -190,15 +190,15 @@ impl fmt::Display for LitKind {
LitKind::Int(n, ty) => {
write!(f, "{n}")?;
match ty {
ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name())?,
ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name())?,
ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name_str())?,
ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name_str())?,
ast::LitIntType::Unsuffixed => {}
}
}
LitKind::Float(symbol, ty) => {
write!(f, "{symbol}")?;
match ty {
ast::LitFloatType::Suffixed(ty) => write!(f, "{}", ty.name())?,
ast::LitFloatType::Suffixed(ty) => write!(f, "{}", ty.name_str())?,
ast::LitFloatType::Unsuffixed => {}
}
}

View file

@ -20,7 +20,6 @@ use rustc_span::{Ident, Span, Symbol};
use thin_vec::ThinVec;
use crate::ast::*;
use crate::ptr::P;
use crate::tokenstream::DelimSpan;
#[derive(Copy, Clone, Debug, PartialEq)]
@ -82,7 +81,7 @@ pub(crate) trait Visitable<'a, V: Visitor<'a>> {
fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result;
}
impl<'a, V: Visitor<'a>, T: ?Sized> Visitable<'a, V> for P<T>
impl<'a, V: Visitor<'a>, T: ?Sized> Visitable<'a, V> for Box<T>
where
T: Visitable<'a, V>,
{
@ -322,7 +321,7 @@ macro_rules! common_visitor_and_walkers {
Fn(FnCtxt, &'a $($mut)? Visibility, &'a $($mut)? Fn),
/// E.g., `|x, y| body`.
Closure(&'a $($mut)? ClosureBinder, &'a $($mut)? Option<CoroutineKind>, &'a $($mut)? P<FnDecl>, &'a $($mut)? P<Expr>),
Closure(&'a $($mut)? ClosureBinder, &'a $($mut)? Option<CoroutineKind>, &'a $($mut)? Box<FnDecl>, &'a $($mut)? Box<Expr>),
}
impl<'a> FnKind<'a> {
@ -369,6 +368,7 @@ macro_rules! common_visitor_and_walkers {
crate::tokenstream::TokenStream,
Movability,
Mutability,
Pinnedness,
Result<(), rustc_span::ErrorGuaranteed>,
rustc_data_structures::fx::FxHashMap<Symbol, usize>,
rustc_span::ErrorGuaranteed,
@ -390,9 +390,9 @@ macro_rules! common_visitor_and_walkers {
ThinVec<(NodeId, Path)>,
ThinVec<PathSegment>,
ThinVec<PreciseCapturingArg>,
ThinVec<P<Pat>>,
ThinVec<P<Ty>>,
ThinVec<P<TyPat>>,
ThinVec<Pat>,
ThinVec<Box<Ty>>,
ThinVec<TyPat>,
);
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@ -472,8 +472,6 @@ macro_rules! common_visitor_and_walkers {
TraitBoundModifiers,
TraitObjectSyntax,
TyAlias,
TyAliasWhereClause,
TyAliasWhereClauses,
TyKind,
TyPatKind,
UnOp,
@ -676,11 +674,11 @@ macro_rules! common_visitor_and_walkers {
// Do nothing.
}
fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
fn flat_map_foreign_item(&mut self, ni: Box<ForeignItem>) -> SmallVec<[Box<ForeignItem>; 1]> {
walk_flat_map_foreign_item(self, ni)
}
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
fn flat_map_item(&mut self, i: Box<Item>) -> SmallVec<[Box<Item>; 1]> {
walk_flat_map_item(self, i)
}
@ -690,9 +688,9 @@ macro_rules! common_visitor_and_walkers {
fn flat_map_assoc_item(
&mut self,
i: P<AssocItem>,
i: Box<AssocItem>,
ctxt: AssocCtxt,
) -> SmallVec<[P<AssocItem>; 1]> {
) -> SmallVec<[Box<AssocItem>; 1]> {
walk_flat_map_assoc_item(self, i, ctxt)
}
@ -704,7 +702,7 @@ macro_rules! common_visitor_and_walkers {
walk_flat_map_arm(self, arm)
}
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
fn filter_map_expr(&mut self, e: Box<Expr>) -> Option<Box<Expr>> {
walk_filter_map_expr(self, e)
}
@ -836,8 +834,8 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, impl_),
ItemKind::Trait(trait_) =>
visit_visitable!($($mut)? vis, trait_),
ItemKind::TraitAlias(ident, generics, bounds) => {
visit_visitable!($($mut)? vis, ident, generics);
ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds}) => {
visit_visitable!($($mut)? vis, constness, ident, generics);
visit_visitable_with!($($mut)? vis, bounds, BoundKind::Bound)
}
ItemKind::MacCall(m) =>
@ -930,8 +928,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()
});
@ -1144,15 +1147,15 @@ macro_rules! generate_list_visit_fns {
}
generate_list_visit_fns! {
visit_items, P<Item>, visit_item;
visit_foreign_items, P<ForeignItem>, visit_foreign_item;
visit_items, Box<Item>, visit_item;
visit_foreign_items, Box<ForeignItem>, visit_foreign_item;
visit_generic_params, GenericParam, visit_generic_param;
visit_stmts, Stmt, visit_stmt;
visit_exprs, P<Expr>, visit_expr;
visit_exprs, Box<Expr>, visit_expr;
visit_expr_fields, ExprField, visit_expr_field;
visit_pat_fields, PatField, visit_pat_field;
visit_variants, Variant, visit_variant;
visit_assoc_items, P<AssocItem>, visit_assoc_item, ctxt: AssocCtxt;
visit_assoc_items, Box<AssocItem>, visit_assoc_item, ctxt: AssocCtxt;
visit_where_predicates, WherePredicate, visit_where_predicate;
visit_params, Param, visit_param;
visit_field_defs, FieldDef, visit_field_def;

View file

@ -8,12 +8,16 @@ edition = "2024"
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
rustc_macros = { path = "../rustc_macros", optional = true }
rustc_serialize = { path = "../rustc_serialize", optional = true }
rustc_span = { path = "../rustc_span", optional = true }
# tidy-alphabetical-end
[features]
# tidy-alphabetical-start
default = ["nightly"]
nightly = [
"dep:rustc_serialize",
"dep:rustc_data_structures",
"dep:rustc_macros",
"dep:rustc_serialize",
"dep:rustc_span",
]
# tidy-alphabetical-end

View file

@ -11,11 +11,221 @@
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
// tidy-alphabetical-end
use std::fmt;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
#[cfg(feature = "nightly")]
use rustc_span::{Symbol, sym};
pub mod visit;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
)]
pub enum IntTy {
Isize,
I8,
I16,
I32,
I64,
I128,
}
impl IntTy {
pub fn name_str(&self) -> &'static str {
match *self {
IntTy::Isize => "isize",
IntTy::I8 => "i8",
IntTy::I16 => "i16",
IntTy::I32 => "i32",
IntTy::I64 => "i64",
IntTy::I128 => "i128",
}
}
#[cfg(feature = "nightly")]
pub fn name(self) -> Symbol {
match self {
IntTy::Isize => sym::isize,
IntTy::I8 => sym::i8,
IntTy::I16 => sym::i16,
IntTy::I32 => sym::i32,
IntTy::I64 => sym::i64,
IntTy::I128 => sym::i128,
}
}
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
IntTy::Isize => return None,
IntTy::I8 => 8,
IntTy::I16 => 16,
IntTy::I32 => 32,
IntTy::I64 => 64,
IntTy::I128 => 128,
})
}
pub fn normalize(&self, target_width: u16) -> Self {
match self {
IntTy::Isize => match target_width {
16 => IntTy::I16,
32 => IntTy::I32,
64 => IntTy::I64,
_ => unreachable!(),
},
_ => *self,
}
}
pub fn to_unsigned(self) -> UintTy {
match self {
IntTy::Isize => UintTy::Usize,
IntTy::I8 => UintTy::U8,
IntTy::I16 => UintTy::U16,
IntTy::I32 => UintTy::U32,
IntTy::I64 => UintTy::U64,
IntTy::I128 => UintTy::U128,
}
}
}
impl fmt::Debug for IntTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name_str())
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
)]
pub enum UintTy {
Usize,
U8,
U16,
U32,
U64,
U128,
}
impl UintTy {
pub fn name_str(&self) -> &'static str {
match *self {
UintTy::Usize => "usize",
UintTy::U8 => "u8",
UintTy::U16 => "u16",
UintTy::U32 => "u32",
UintTy::U64 => "u64",
UintTy::U128 => "u128",
}
}
#[cfg(feature = "nightly")]
pub fn name(self) -> Symbol {
match self {
UintTy::Usize => sym::usize,
UintTy::U8 => sym::u8,
UintTy::U16 => sym::u16,
UintTy::U32 => sym::u32,
UintTy::U64 => sym::u64,
UintTy::U128 => sym::u128,
}
}
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
UintTy::Usize => return None,
UintTy::U8 => 8,
UintTy::U16 => 16,
UintTy::U32 => 32,
UintTy::U64 => 64,
UintTy::U128 => 128,
})
}
pub fn normalize(&self, target_width: u16) -> Self {
match self {
UintTy::Usize => match target_width {
16 => UintTy::U16,
32 => UintTy::U32,
64 => UintTy::U64,
_ => unreachable!(),
},
_ => *self,
}
}
pub fn to_signed(self) -> IntTy {
match self {
UintTy::Usize => IntTy::Isize,
UintTy::U8 => IntTy::I8,
UintTy::U16 => IntTy::I16,
UintTy::U32 => IntTy::I32,
UintTy::U64 => IntTy::I64,
UintTy::U128 => IntTy::I128,
}
}
}
impl fmt::Debug for UintTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name_str())
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
)]
pub enum FloatTy {
F16,
F32,
F64,
F128,
}
impl FloatTy {
pub fn name_str(self) -> &'static str {
match self {
FloatTy::F16 => "f16",
FloatTy::F32 => "f32",
FloatTy::F64 => "f64",
FloatTy::F128 => "f128",
}
}
#[cfg(feature = "nightly")]
pub fn name(self) -> Symbol {
match self {
FloatTy::F16 => sym::f16,
FloatTy::F32 => sym::f32,
FloatTy::F64 => sym::f64,
FloatTy::F128 => sym::f128,
}
}
pub fn bit_width(self) -> u64 {
match self {
FloatTy::F16 => 16,
FloatTy::F32 => 32,
FloatTy::F64 => 64,
FloatTy::F128 => 128,
}
}
}
impl fmt::Debug for FloatTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name_str())
}
}
/// The movability of a coroutine / closure literal:
/// whether a coroutine contains self-references, causing it to be `!Unpin`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
@ -101,3 +311,10 @@ pub enum Pinnedness {
Not,
Pinned,
}
impl Pinnedness {
/// Return `true` if self is pinned
pub fn is_pinned(self) -> bool {
matches!(self, Self::Pinned)
}
}

View file

@ -11,7 +11,6 @@ doctest = false
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }

View file

@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| asm::InlineAsmArch::Arm64EC
| asm::InlineAsmArch::RiscV32
| asm::InlineAsmArch::RiscV64
| asm::InlineAsmArch::LoongArch32
| asm::InlineAsmArch::LoongArch64
| asm::InlineAsmArch::S390x
);

View file

@ -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;
@ -26,7 +27,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
}
fn lower_stmts(
pub(super) fn lower_stmts(
&mut self,
mut ast_stmts: &[Stmt],
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
@ -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 })
}

View file

@ -0,0 +1,345 @@
use std::sync::Arc;
use thin_vec::thin_vec;
use crate::LoweringContext;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// Lowered contracts are guarded with the `contract_checks` compiler flag,
/// i.e. the flag turns into a boolean guard in the lowered HIR. The reason
/// for not eliminating the contract code entirely when the `contract_checks`
/// flag is disabled is so that contracts can be type checked, even when
/// they are disabled, which avoids them becoming stale (i.e. out of sync
/// with the codebase) over time.
///
/// The optimiser should be able to eliminate all contract code guarded
/// by `if false`, leaving the original body intact when runtime contract
/// checks are disabled.
pub(super) fn lower_contract(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract: &rustc_ast::FnContract,
) -> rustc_hir::Expr<'hir> {
// The order in which things are lowered is important! I.e to
// refer to variables in contract_decls from postcond/precond,
// we must lower it first!
let contract_decls = self.lower_stmts(&contract.declarations).0;
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// let __postcond = if contract_checks {
// CONTRACT_DECLARATIONS;
// contract_check_requires(PRECOND);
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// {
// let ret = { body };
//
// if contract_checks {
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let precond = self.lower_precond(req);
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check = self.lower_contract_check_with_postcond(
contract_decls,
Some(precond),
postcond_checker,
);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(None, Some(ens)) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// let __postcond = if contract_checks {
// Some(|ret_val| POSTCOND)
// } else {
// None
// };
// {
// let ret = { body };
//
// if contract_checks {
// CONTRACT_DECLARATIONS;
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let postcond_checker = self.lower_postcond_checker(ens);
let contract_check =
self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker);
let wrapped_body =
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
self.expr_block(wrapped_body)
}
(Some(req), None) => {
// Lower the fn contract, which turns:
//
// { body }
//
// into:
//
// {
// if contracts_checks {
// CONTRACT_DECLARATIONS;
// contract_requires(PRECOND);
// }
// body
// }
let precond = self.lower_precond(req);
let precond_check = self.lower_contract_check_just_precond(contract_decls, precond);
let body = self.arena.alloc(body(self));
// Flatten the body into precond check, then body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([precond_check].into_iter()),
Some(body),
);
self.expr_block(wrapped_body)
}
(None, None) => body(self),
}
}
/// Lower the precondition check intrinsic.
fn lower_precond(&mut self, req: &Box<rustc_ast::Expr>) -> rustc_hir::Stmt<'hir> {
let lowered_req = self.lower_expr_mut(&req);
let req_span = self.mark_span_with_reason(
rustc_span::DesugaringKind::Contract,
lowered_req.span,
Some(Arc::clone(&self.allow_contracts)),
);
let precond = self.expr_call_lang_item_fn_mut(
req_span,
rustc_hir::LangItem::ContractCheckRequires,
&*arena_vec![self; lowered_req],
);
self.stmt_expr(req.span, precond)
}
fn lower_postcond_checker(
&mut self,
ens: &Box<rustc_ast::Expr>,
) -> &'hir rustc_hir::Expr<'hir> {
let ens_span = self.lower_span(ens.span);
let ens_span = self.mark_span_with_reason(
rustc_span::DesugaringKind::Contract,
ens_span,
Some(Arc::clone(&self.allow_contracts)),
);
let lowered_ens = self.lower_expr_mut(&ens);
self.expr_call_lang_item_fn(
ens_span,
rustc_hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![self; lowered_ens],
)
}
fn lower_contract_check_just_precond(
&mut self,
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
precond: rustc_hir::Stmt<'hir>,
) -> rustc_hir::Stmt<'hir> {
let stmts = self
.arena
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter()));
let then_block_stmts = self.block_all(precond.span, stmts, None);
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let precond_check = rustc_hir::ExprKind::If(
self.arena.alloc(self.expr_bool_literal(precond.span, self.tcx.sess.contract_checks())),
then_block,
None,
);
let precond_check = self.expr(precond.span, precond_check);
self.stmt_expr(precond.span, precond_check)
}
fn lower_contract_check_with_postcond(
&mut self,
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
precond: Option<rustc_hir::Stmt<'hir>>,
postcond_checker: &'hir rustc_hir::Expr<'hir>,
) -> &'hir rustc_hir::Expr<'hir> {
let stmts = self
.arena
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter()));
let span = match precond {
Some(precond) => precond.span,
None => postcond_checker.span,
};
let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionSome,
&*arena_vec![self; *postcond_checker],
));
let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker));
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item(
postcond_checker.span,
rustc_hir::lang_items::LangItem::OptionNone,
Default::default(),
));
let else_block = self.block_expr(none_expr);
let else_block = self.arena.alloc(self.expr_block(else_block));
let contract_check = rustc_hir::ExprKind::If(
self.arena.alloc(self.expr_bool_literal(span, self.tcx.sess.contract_checks())),
then_block,
Some(else_block),
);
self.arena.alloc(self.expr(span, contract_check))
}
fn wrap_body_with_contract_check(
&mut self,
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
contract_check: &'hir rustc_hir::Expr<'hir>,
postcond_span: rustc_span::Span,
) -> &'hir rustc_hir::Block<'hir> {
let check_ident: rustc_span::Ident =
rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span);
let (check_hir_id, postcond_decl) = {
// Set up the postcondition `let` statement.
let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut(
postcond_span,
check_ident,
rustc_hir::BindingMode::NONE,
);
(
check_hir_id,
self.stmt_let_pat(
None,
postcond_span,
Some(contract_check),
self.arena.alloc(checker_pat),
rustc_hir::LocalSource::Contract,
),
)
};
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
self.contract_ensures = Some((postcond_span, check_ident, check_hir_id));
let body = self.arena.alloc(body(self));
// Finally, inject an ensures check on the implicit return of the body.
let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id);
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = self.block_all(
body.span,
self.arena.alloc_from_iter([postcond_decl].into_iter()),
Some(body),
);
wrapped_body
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
pub(super) fn checked_return(
&mut self,
opt_expr: Option<&'hir rustc_hir::Expr<'hir>>,
) -> rustc_hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
rustc_hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(super) fn inject_ensures_check(
&mut self,
expr: &'hir rustc_hir::Expr<'hir>,
span: rustc_span::Span,
cond_ident: rustc_span::Ident,
cond_hir_id: rustc_hir::HirId,
) -> &'hir rustc_hir::Expr<'hir> {
// {
// let ret = { body };
//
// if contract_checks {
// contract_check_ensures(__postcond, ret)
// } else {
// ret
// }
// }
let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span);
// Set up the return `let` statement.
let (ret_pat, ret_hir_id) =
self.pat_ident_binding_mode_mut(span, ret_ident, rustc_hir::BindingMode::NONE);
let ret_stmt = self.stmt_let_pat(
None,
span,
Some(expr),
self.arena.alloc(ret_pat),
rustc_hir::LocalSource::Contract,
);
let ret = self.expr_ident(span, ret_ident, ret_hir_id);
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let contract_check = self.expr_call_lang_item_fn_mut(
span,
rustc_hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *ret],
);
let contract_check = self.arena.alloc(contract_check);
let call_expr = self.block_expr_block(contract_check);
// same ident can't be used in 2 places, so we create a new one for the
// else branch
let ret = self.expr_ident(span, ret_ident, ret_hir_id);
let ret_block = self.block_expr_block(ret);
let contracts_enabled: rustc_hir::Expr<'_> =
self.expr_bool_literal(span, self.tcx.sess.contract_checks());
let contract_check = self.arena.alloc(self.expr(
span,
rustc_hir::ExprKind::If(
self.arena.alloc(contracts_enabled),
call_expr,
Some(ret_block),
),
));
let attrs: rustc_ast::AttrVec = thin_vec![self.unreachable_code_attr(span)];
self.lower_attrs(contract_check.hir_id, &attrs, span, rustc_hir::Target::Expression);
let ret_block = self.block_all(span, arena_vec![self; ret_stmt], Some(contract_check));
self.arena.alloc(self.expr_block(self.arena.alloc(ret_block)))
}
}

View file

@ -1,14 +1,14 @@
use std::ops::ControlFlow;
use std::sync::Arc;
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::definitions::DefPathData;
use rustc_hir::{HirId, Target, find_attr};
use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::errors::report_lit_error;
@ -53,7 +53,7 @@ impl<'v> rustc_ast::visit::Visitor<'v> for WillCreateDefIdsVisitor {
}
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
fn lower_exprs(&mut self, exprs: &[Box<Expr>]) -> &'hir [hir::Expr<'hir>] {
self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x)))
}
@ -63,19 +63,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
ensure_sufficient_stack(|| {
let mut span = self.lower_span(e.span);
match &e.kind {
// Parenthesis expression does not have a HirId and is handled specially.
ExprKind::Paren(ex) => {
let mut ex = self.lower_expr_mut(ex);
// Include parens in span, but only if it is a super-span.
if e.span.contains(ex.span) {
ex.span = self.lower_span(e.span);
ex.span = self.lower_span(e.span.with_ctxt(ex.span.ctxt()));
}
// Merge attributes into the inner expression.
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);
@ -98,7 +99,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
let expr_hir_id = self.lower_node_id(e.id);
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)),
@ -232,10 +233,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
*fn_arg_span,
),
None => self.lower_expr_closure(
attrs,
binder,
*capture_clause,
e.id,
expr_hir_id,
*constness,
*movability,
fn_decl,
@ -288,7 +289,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_span(*brackets_span),
),
ExprKind::Range(e1, e2, lims) => {
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
self.lower_expr_range(span, e1.as_deref(), e2.as_deref(), *lims)
}
ExprKind::Underscore => {
let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span });
@ -380,40 +382,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span),
};
hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) }
hir::Expr { hir_id: expr_hir_id, kind, span }
})
}
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
/// a contract ensures clause, if it exists.
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
let checked_ret =
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
} else {
opt_expr
};
hir::ExprKind::Ret(checked_ret)
}
/// Wraps an expression with a call to the ensures check before it gets returned.
pub(crate) fn inject_ensures_check(
&mut self,
expr: &'hir hir::Expr<'hir>,
span: Span,
cond_ident: Ident,
cond_hir_id: HirId,
) -> &'hir hir::Expr<'hir> {
let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
let call_expr = self.expr_call_lang_item_fn_mut(
span,
hir::LangItem::ContractCheckEnsures,
arena_vec![self; *cond_fn, *expr],
);
self.arena.alloc(call_expr)
}
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
@ -455,7 +427,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_legacy_const_generics(
&mut self,
mut f: Expr,
args: ThinVec<AstP<Expr>>,
args: ThinVec<Box<Expr>>,
legacy_args_idx: &[usize],
) -> hir::ExprKind<'hir> {
let ExprKind::Path(None, path) = &mut f.kind else {
@ -492,10 +464,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
for (idx, arg) in args.iter().cloned().enumerate() {
if legacy_args_idx.contains(&idx) {
let node_id = self.next_node_id();
self.create_def(node_id, None, DefKind::AnonConst, f.span);
self.create_def(
node_id,
None,
DefKind::AnonConst,
DefPathData::LateAnonConst,
f.span,
);
let mut visitor = WillCreateDefIdsVisitor {};
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
AstP(Expr {
Box::new(Expr {
id: self.next_node_id(),
kind: ExprKind::Err(invalid_expr_error(self.tcx, span)),
span: f.span,
@ -516,7 +494,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Add generic args to the last element of the path.
let last_segment = path.segments.last_mut().unwrap();
assert!(last_segment.args.is_none());
last_segment.args = Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs {
last_segment.args = Some(Box::new(GenericArgs::AngleBracketed(AngleBracketedArgs {
span: DUMMY_SP,
args: generic_args,
})));
@ -640,7 +618,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).
@ -812,7 +790,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(
inner_hir_id,
&[Attribute {
kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(
kind: AttrKind::Normal(Box::new(NormalAttr::from_ident(Ident::new(
sym::track_caller,
span,
)))),
@ -821,6 +799,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: unstable_span,
}],
span,
Target::Fn,
);
}
}
@ -886,6 +865,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
};
let features = match await_kind {
FutureKind::Future if is_async_gen => Some(Arc::clone(&self.allow_async_gen)),
FutureKind::Future => None,
FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)),
};
@ -1052,10 +1032,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_closure(
&mut self,
attrs: &[rustc_hir::Attribute],
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
closure_hir_id: hir::HirId,
constness: Const,
movability: Movability,
decl: &FnDecl,
@ -1067,15 +1047,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
let mut coroutine_kind = if this
.attrs
.get(&closure_hir_id.local_id)
.is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine)))
{
Some(hir::CoroutineKind::Coroutine(Movability::Movable))
} else {
None
};
let mut coroutine_kind = find_attr!(attrs, AttributeKind::Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));
// FIXME(contracts): Support contracts on closures?
let body_id = this.lower_fn_body(decl, None, |this| {
this.coroutine_kind = coroutine_kind;
@ -1269,13 +1243,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
let rhs = self.lower_expr(rhs);
// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
let destructure_let = self.stmt_let_pat(
None,
whole_span,
Some(rhs),
pat,
hir::LocalSource::AssignDesugar(self.lower_span(eq_sign_span)),
);
let destructure_let =
self.stmt_let_pat(None, whole_span, Some(rhs), pat, hir::LocalSource::AssignDesugar);
// `a = lhs1; b = lhs2;`.
let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments));
@ -1291,7 +1260,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn extract_tuple_struct_path<'a>(
&mut self,
expr: &'a Expr,
) -> Option<(&'a Option<AstP<QSelf>>, &'a Path)> {
) -> Option<(&'a Option<Box<QSelf>>, &'a Path)> {
if let ExprKind::Path(qself, path) = &expr.kind {
// Does the path resolve to something disallowed in a tuple struct/variant pattern?
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
@ -1313,7 +1282,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn extract_unit_struct_path<'a>(
&mut self,
expr: &'a Expr,
) -> Option<(&'a Option<AstP<QSelf>>, &'a Path)> {
) -> Option<(&'a Option<Box<QSelf>>, &'a Path)> {
if let ExprKind::Path(qself, path) = &expr.kind {
// Does the path resolve to something disallowed in a unit struct/variant pattern?
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
@ -1440,10 +1409,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);
@ -1484,7 +1453,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Each sub-assignment is recorded in `assignments`.
fn destructure_sequence(
&mut self,
elements: &[AstP<Expr>],
elements: &[Box<Expr>],
ctx: &str,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
@ -1511,7 +1480,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let e1 = self.lower_expr_mut(e1);
let e2 = self.lower_expr_mut(e2);
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span));
let fn_path = self.make_lang_item_qpath(hir::LangItem::RangeInclusiveNew, span, None);
let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
}
@ -1542,7 +1511,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
@ -1566,17 +1541,32 @@ 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 span = self.lower_span(e.span);
let span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
let expr = self.lower_expr(e);
let ident = Ident::new(s, self.lower_span(e.span));
self.expr_field(ident, expr, e.span)
},
),
let ident = Ident::new(s, span);
self.expr_field(ident, expr, span)
}),
);
hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None)),
fields,
hir::StructTailExpr::None,
)
@ -1661,7 +1651,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),
@ -1726,8 +1716,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `yield $expr` is transformed into `task_context = yield async_gen_ready($expr)`.
// This ensures that we store our resumed `ResumeContext` correctly, and also that
// the apparent value of the `yield` expression is `()`.
let wrapped_yielded = self.expr_call_lang_item_fn(
let desugar_span = self.mark_span_with_reason(
DesugaringKind::Async,
span,
Some(Arc::clone(&self.allow_async_gen)),
);
let wrapped_yielded = self.expr_call_lang_item_fn(
desugar_span,
hir::LangItem::AsyncGenReady,
std::slice::from_ref(yielded),
);
@ -1739,7 +1734,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
unreachable!("use of `await` outside of an async context.");
};
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
let lhs = self.expr_ident(desugar_span, task_context_ident, task_context_hid);
hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span))
} else {
@ -1917,7 +1912,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
}
@ -1958,23 +1953,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
};
// `#[allow(unreachable_code)]`
let attr = attr::mk_attr_nested_word(
&self.tcx.sess.psess.attr_id_generator,
AttrStyle::Outer,
Safety::Default,
sym::allow,
sym::unreachable_code,
try_span,
);
let attrs: AttrVec = thin_vec![attr];
let attrs: AttrVec = thin_vec![self.unreachable_code_attr(try_span)];
// `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
let continue_arm = {
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)
};
@ -2005,7 +1991,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)
@ -2107,7 +2093,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e))
}
fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
}
@ -2148,6 +2134,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::Call(e, args))
}
pub(super) fn expr_struct(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::ExprField<'hir>],
) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Struct(path, fields, rustc_hir::StructTailExpr::None))
}
pub(super) fn expr_enum_variant(
&mut self,
span: Span,
path: &'hir hir::QPath<'hir>,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let fields = self.arena.alloc_from_iter(fields.into_iter().enumerate().map(|(i, f)| {
hir::ExprField {
hir_id: self.next_id(),
ident: Ident::from_str(&i.to_string()),
expr: f,
span: f.span,
is_shorthand: false,
}
}));
self.expr_struct(span, path, fields)
}
pub(super) fn expr_enum_variant_lang_item(
&mut self,
span: Span,
lang_item: hir::LangItem,
fields: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let path = self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None));
self.expr_enum_variant(span, path, fields)
}
pub(super) fn expr_call(
&mut self,
span: Span,
@ -2176,8 +2199,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args))
}
fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span))))
pub(super) fn expr_lang_item_path(
&mut self,
span: Span,
lang_item: hir::LangItem,
) -> hir::Expr<'hir> {
let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
self.expr(span, hir::ExprKind::Path(qpath))
}
/// `<LangItem>::name`
@ -2257,6 +2285,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(b.span, hir::ExprKind::Block(b, None))
}
/// Wrap an expression in a block, and wrap that block in an expression again.
/// Useful for constructing if-expressions, which require expressions of
/// kind block.
pub(super) fn block_expr_block(
&mut self,
expr: &'hir hir::Expr<'hir>,
) -> &'hir hir::Expr<'hir> {
let b = self.block_expr(expr);
self.arena.alloc(self.expr_block(b))
}
pub(super) fn expr_array_ref(
&mut self,
span: Span,
@ -2270,6 +2309,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
}
pub(super) fn expr_bool_literal(&mut self, span: Span, val: bool) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Lit(Spanned { node: LitKind::Bool(val), span }))
}
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
let hir_id = self.next_id();
hir::Expr { hir_id, kind, span: self.lower_span(span) }
@ -2303,6 +2346,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: expr,
}
}
/// `#[allow(unreachable_code)]`
pub(super) fn unreachable_code_attr(&mut self, span: Span) -> Attribute {
let attr = attr::mk_attr_nested_word(
&self.tcx.sess.psess.attr_id_generator,
AttrStyle::Outer,
Safety::Default,
sym::allow,
sym::unreachable_code,
span,
);
attr
}
}
/// Used by [`LoweringContext::make_lowered_await`] to customize the desugaring based on what kind

View file

@ -391,7 +391,8 @@ fn make_format_spec<'hir>(
let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp);
let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp);
let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp);
let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp));
let placeholder =
ctx.arena.alloc(ctx.make_lang_item_qpath(hir::LangItem::FormatPlaceholder, sp, None));
let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]);
ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None))
}
@ -487,26 +488,6 @@ fn expand_format_args<'hir>(
// Generate:
// []
(vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
} else if argmap.len() == 1 && arguments.len() == 1 {
// Only one argument, so we don't need to make the `args` tuple.
//
// Generate:
// super let args = [<core::fmt::Argument>::new_display(&arg)];
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|(&(arg_index, ty), &placeholder_span)| {
let arg = &arguments[arg_index];
let placeholder_span =
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
let arg = ctx.lower_expr(&arg.expr);
let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
make_argument(ctx, placeholder_span, ref_arg, ty)
},
));
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let args_ident = Ident::new(sym::args, macsp);
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
(vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
} else {
// Generate:
// super let args = (&arg0, &arg1, &…);

View file

@ -311,7 +311,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
);
self.with_parent(const_arg.hir_id, |this| {
intravisit::walk_ambig_const_arg(this, const_arg);
intravisit::walk_const_arg(this, const_arg);
});
}

View file

@ -1,12 +1,13 @@
use rustc_abi::ExternAbi;
use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_attr_data_structures::{AttributeKind, find_attr};
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};
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};
@ -35,20 +36,18 @@ pub(super) struct ItemLowerer<'a, 'hir> {
/// clause if it exists.
fn add_ty_alias_where_clause(
generics: &mut ast::Generics,
mut where_clauses: TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
prefer_first: bool,
) {
generics.where_clause.predicates.extend_from_slice(&after_where_clause.predicates);
let mut before = (generics.where_clause.has_where_token, generics.where_clause.span);
let mut after = (after_where_clause.has_where_token, after_where_clause.span);
if !prefer_first {
(where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before);
(before, after) = (after, before);
}
let where_clause =
if where_clauses.before.has_where_token || !where_clauses.after.has_where_token {
where_clauses.before
} else {
where_clauses.after
};
generics.where_clause.has_where_token = where_clause.has_where_token;
generics.where_clause.span = where_clause.span;
(generics.where_clause.has_where_token, generics.where_clause.span) =
if before.0 || !after.0 { before } else { after };
}
impl<'a, 'hir> ItemLowerer<'a, 'hir> {
@ -81,7 +80,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)
})
}
@ -102,7 +101,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
impl<'hir> LoweringContext<'_, 'hir> {
pub(super) fn lower_mod(
&mut self,
items: &[P<Item>],
items: &[Box<Item>],
spans: &ModSpans,
) -> &'hir hir::Mod<'hir> {
self.arena.alloc(hir::Mod {
@ -137,7 +136,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(),
@ -252,7 +251,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"),
@ -270,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
hir::ItemKind::GlobalAsm { asm, fake_body }
}
ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
ItemKind::TyAlias(box TyAlias { ident, generics, after_where_clause, ty, .. }) => {
// We lower
//
// type Foo = impl Trait
@ -281,7 +280,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// opaque type Foo1: Trait
let ident = self.lower_ident(*ident);
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
add_ty_alias_where_clause(&mut generics, after_where_clause, true);
let (generics, ty) = self.lower_generics(
&generics,
id,
@ -341,13 +340,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,
}) => {
@ -365,54 +360,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,
@ -444,7 +415,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ItemKind::Trait(constness, *is_auto, safety, ident, generics, bounds, items)
}
ItemKind::TraitAlias(ident, generics, bounds) => {
ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds }) => {
let constness = self.lower_constness(*constness);
let ident = self.lower_ident(*ident);
let (generics, bounds) = self.lower_generics(
generics,
@ -453,26 +425,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|this| {
this.lower_param_bounds(
bounds,
RelaxedBoundPolicy::Allowed,
RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::TraitAlias),
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
)
},
);
hir::ItemKind::TraitAlias(ident, generics, bounds)
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
let ident = self.lower_ident(*ident);
let body = P(self.lower_delim_args(body));
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);
@ -650,7 +622,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;
@ -719,7 +692,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),
@ -802,7 +775,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,
@ -821,7 +794,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 {
@ -922,10 +900,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Type(box TyAlias {
ident, generics, where_clauses, bounds, ty, ..
ident,
generics,
after_where_clause,
bounds,
ty,
..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
let (generics, kind) = self.lower_generics(
&generics,
i.id,
@ -983,6 +966,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,
@ -992,7 +1013,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 {
@ -1048,9 +1074,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, (generics, hir::ImplItemKind::Fn(sig, body_id)))
}
AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
AssocItemKind::Type(box TyAlias {
ident, generics, after_where_clause, ty, ..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
(
*ident,
self.lower_generics(
@ -1097,20 +1125,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)
}
@ -1162,7 +1201,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, &param.attrs, param.span);
self.lower_attrs(hir_id, &param.attrs, param.span, Target::Param);
hir::Param {
hir_id,
pat: self.lower_pat(&param.pat),
@ -1181,76 +1220,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
// Optionally lower the fn contract, which turns:
//
// { body }
//
// into:
//
// { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
// Optionally lower the fn contract
if let Some(contract) = contract {
let precond = if let Some(req) = &contract.requires {
// Lower the precondition check intrinsic.
let lowered_req = this.lower_expr_mut(&req);
let req_span = this.mark_span_with_reason(
DesugaringKind::Contract,
lowered_req.span,
None,
);
let precond = this.expr_call_lang_item_fn_mut(
req_span,
hir::LangItem::ContractCheckRequires,
&*arena_vec![this; lowered_req],
);
Some(this.stmt_expr(req.span, precond))
} else {
None
};
let (postcond, body) = if let Some(ens) = &contract.ensures {
let ens_span = this.lower_span(ens.span);
let ens_span =
this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None);
// Set up the postcondition `let` statement.
let check_ident: Ident =
Ident::from_str_and_span("__ensures_checker", ens_span);
let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut(
ens_span,
check_ident,
hir::BindingMode::NONE,
);
let lowered_ens = this.lower_expr_mut(&ens);
let postcond_checker = this.expr_call_lang_item_fn(
ens_span,
hir::LangItem::ContractBuildCheckEnsures,
&*arena_vec![this; lowered_ens],
);
let postcond = this.stmt_let_pat(
None,
ens_span,
Some(postcond_checker),
this.arena.alloc(checker_pat),
hir::LocalSource::Contract,
);
// Install contract_ensures so we will intercept `return` statements,
// then lower the body.
this.contract_ensures = Some((ens_span, check_ident, check_hir_id));
let body = this.arena.alloc(body(this));
// Finally, inject an ensures check on the implicit return of the body.
let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id);
(Some(postcond), body)
} else {
let body = &*this.arena.alloc(body(this));
(None, body)
};
// Flatten the body into precond, then postcond, then wrapped body.
let wrapped_body = this.block_all(
body.span,
this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()),
Some(body),
);
(params, this.expr_block(wrapped_body))
(params, this.lower_contract(body, contract))
} else {
(params, body(this))
}
@ -1576,7 +1548,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
{
@ -1842,7 +1814,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,

View file

@ -51,10 +51,11 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
use rustc_hir::definitions::{DefPathData, DisambiguatorState};
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;
@ -77,6 +78,7 @@ macro_rules! arena_vec {
mod asm;
mod block;
mod contract;
mod delegation;
mod errors;
mod expr;
@ -92,6 +94,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
struct LoweringContext<'a, 'hir> {
tcx: TyCtxt<'hir>,
resolver: &'a mut ResolverAstLowering,
disambiguator: DisambiguatorState,
/// Used to allocate HIR nodes.
arena: &'hir hir::Arena<'hir>,
@ -135,9 +138,11 @@ struct LoweringContext<'a, 'hir> {
#[cfg(debug_assertions)]
node_id_to_local_id: NodeMap<hir::ItemLocalId>,
allow_contracts: Arc<[Symbol]>,
allow_try_trait: Arc<[Symbol]>,
allow_gen_future: Arc<[Symbol]>,
allow_pattern_type: Arc<[Symbol]>,
allow_async_gen: Arc<[Symbol]>,
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
@ -154,6 +159,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Pseudo-globals.
tcx,
resolver,
disambiguator: DisambiguatorState::new(),
arena: tcx.hir_arena,
// HirId handling.
@ -179,6 +185,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
current_item: None,
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_contracts: [sym::contracts_internals].into(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
@ -186,8 +193,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
[sym::gen_future].into()
},
allow_for_await: [sym::async_iterator].into(),
allow_for_await: [sym::async_gen_internals, sym::async_iterator].into(),
allow_async_fn_traits: [sym::async_fn_traits].into(),
allow_async_gen: [sym::async_gen_internals].into(),
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
@ -296,6 +304,8 @@ enum RelaxedBoundPolicy<'a> {
enum RelaxedBoundForbiddenReason {
TraitObjectTy,
SuperTrait,
TraitAlias,
AssocTyBounds,
LateBoundVarsInScope,
}
@ -543,6 +553,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
node_id: ast::NodeId,
name: Option<Symbol>,
def_kind: DefKind,
def_path_data: DefPathData,
span: Span,
) -> LocalDefId {
let parent = self.current_hir_id_owner.def_id;
@ -558,7 +569,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let def_id = self
.tcx
.at(span)
.create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator)
.create_def(parent, name, def_kind, Some(def_path_data), &mut self.disambiguator)
.def_id();
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
@ -675,7 +686,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let bodies = SortedMap::from_presorted_elements(bodies);
// Don't hash unless necessary, because it's expensive.
let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) =
let rustc_middle::hir::Hashes { opt_hash_including_bodies, attrs_hash, delayed_lints_hash } =
self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque);
let num_nodes = self.item_local_id_counter.as_usize();
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
@ -843,6 +854,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param,
Some(kw::UnderscoreLifetime),
DefKind::LifetimeParam,
DefPathData::DesugaredAnonymousLifetime,
ident.span,
);
debug!(?_def_id);
@ -942,11 +954,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 +985,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 +1125,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 }
}
}
@ -1217,7 +1235,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_path_ty(
&mut self,
t: &Ty,
qself: &Option<ptr::P<QSelf>>,
qself: &Option<Box<QSelf>>,
path: &Path,
param_mode: ParamMode,
itctx: ImplTraitContext,
@ -1939,7 +1957,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, &param.attrs, param.span());
self.lower_attrs(hir_id, &param.attrs, param.span(), Target::Param);
hir::GenericParam {
hir_id,
def_id: self.local_def_id(param.id),
@ -2021,7 +2039,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 },
)
}
}
@ -2078,12 +2096,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: Span,
rbp: RelaxedBoundPolicy<'_>,
) {
// Even though feature `more_maybe_bounds` bypasses the given policy and (currently) enables
// relaxed bounds in every conceivable position[^1], we don't want to advertise it to the user
// (via a feature gate) since it's super internal. Besides this, it'd be quite distracting.
// Even though feature `more_maybe_bounds` enables the user to relax all default bounds
// other than `Sized` in a lot more positions (thereby bypassing the given policy), we don't
// want to advertise it to the user (via a feature gate error) since it's super internal.
//
// [^1]: Strictly speaking, this is incorrect (at the very least for `Sized`) because it's
// no longer fully consistent with default trait elaboration in HIR ty lowering.
// FIXME(more_maybe_bounds): Moreover, if we actually were to add proper default traits
// (like a hypothetical `Move` or `Leak`) we would want to validate the location according
// to default trait elaboration in HIR ty lowering (which depends on the specific trait in
// question: E.g., `?Sized` & `?Move` most likely won't be allowed in all the same places).
match rbp {
RelaxedBoundPolicy::Allowed => return,
@ -2094,37 +2114,47 @@ 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;
}
let gate = |context, subject| {
let extended = self.tcx.features().more_maybe_bounds();
let is_sized = trait_ref
.trait_def_id()
.is_some_and(|def_id| self.tcx.is_lang_item(def_id, hir::LangItem::Sized));
if extended && !is_sized {
return;
}
let prefix = if extended { "`Sized` " } else { "" };
let mut diag = self.dcx().struct_span_err(
span,
format!("relaxed {prefix}bounds are not permitted in {context}"),
);
if is_sized {
diag.note(format!(
"{subject} are not implicitly bounded by `Sized`, \
so there is nothing to relax"
));
}
diag.emit();
};
match reason {
RelaxedBoundForbiddenReason::TraitObjectTy => {
self.dcx().span_err(
span,
"relaxed bounds are not permitted in trait object types",
);
gate("trait object types", "trait object types");
return;
}
RelaxedBoundForbiddenReason::SuperTrait => {
let mut diag = self.dcx().struct_span_err(
span,
"relaxed bounds are not permitted in supertrait bounds",
);
if let Some(def_id) = trait_ref.trait_def_id()
&& self.tcx.is_lang_item(def_id, hir::LangItem::Sized)
{
diag.note("traits are `?Sized` by default");
}
diag.emit();
gate("supertrait bounds", "traits");
return;
}
RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
RelaxedBoundForbiddenReason::TraitAlias => {
gate("trait alias bounds", "trait aliases");
return;
}
RelaxedBoundForbiddenReason::AssocTyBounds
| RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
};
}
}
@ -2133,7 +2163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.struct_span_err(span, "this relaxed bound is not permitted here")
.with_note(
"in this context, relaxed bounds are only allowed on \
type parameters defined by the closest item",
type parameters defined on the closest item",
)
.emit();
}
@ -2269,7 +2299,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We're lowering a const argument that was originally thought to be a type argument,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
let def_id = self.create_def(
node_id,
None,
DefKind::AnonConst,
DefPathData::LateAnonConst,
span,
);
let hir_id = self.lower_node_id(node_id);
let path_expr = Expr {
@ -2499,8 +2535,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
lang_item: hir::LangItem,
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))
let path = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
self.pat(span, hir::PatKind::Struct(path, fields, None))
}
fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) {

View file

@ -1,10 +1,10 @@
use std::sync::Arc;
use rustc_ast::ptr::P;
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::definitions::DefPathData;
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};
@ -94,7 +94,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,
@ -107,10 +107,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) => {
@ -143,7 +144,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
// return inner to be processed in next loop
PatKind::Paren(inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
PatKind::MacCall(_) => {
panic!("{pattern:#?} shouldn't exist here")
}
PatKind::Err(guar) => break hir::PatKind::Err(*guar),
}
};
@ -154,7 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_pat_tuple(
&mut self,
pats: &[P<Pat>],
pats: &[Pat],
ctx: &str,
) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {
let mut elems = Vec::with_capacity(pats.len());
@ -209,7 +212,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// When encountering `($binding_mode $ident @)? ..` (`slice`),
/// this is interpreted as a sub-slice pattern semantically.
/// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
fn lower_pat_slice(&mut self, pats: &[Pat]) -> hir::PatKind<'hir> {
let mut before = Vec::new();
let mut after = Vec::new();
let mut slice = None;
@ -460,6 +463,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}),
),
TyPatKind::NotNull => hir::TyPatKind::NotNull,
TyPatKind::Or(variants) => {
hir::TyPatKind::Or(self.arena.alloc_from_iter(
variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
@ -524,7 +528,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We're generating a range end that didn't exist in the AST,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
let def_id =
self.create_def(node_id, None, DefKind::AnonConst, DefPathData::LateAnonConst, span);
let hir_id = self.lower_node_id(node_id);
let unstable_span = self.mark_span_with_reason(

View file

@ -24,7 +24,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
pub(crate) fn lower_qpath(
&mut self,
id: NodeId,
qself: &Option<ptr::P<QSelf>>,
qself: &Option<Box<QSelf>>,
p: &Path,
param_mode: ParamMode,
allow_return_type_notation: AllowReturnTypeNotation,

View file

@ -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" }

View file

@ -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
@ -40,14 +51,12 @@ ast_passes_auto_generic = auto traits cannot have generic parameters
ast_passes_auto_items = auto traits cannot have associated items
.label = {ast_passes_auto_items}
.suggestion = remove these associated items
.suggestion = remove the associated items
ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetime bounds
.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
@ -241,6 +268,10 @@ ast_passes_tilde_const_disallowed = `[const]` is not allowed here
.trait_assoc_ty = associated types in non-`const` traits cannot have `[const]` trait bounds
.trait_impl_assoc_ty = associated types in non-const impls cannot have `[const]` trait bounds
.inherent_assoc_ty = inherent associated types cannot have `[const]` trait bounds
.struct = structs cannot have `[const]` trait bounds
.enum = enums cannot have `[const]` trait bounds
.union = unions cannot have `[const]` trait bounds
.anon_const = anonymous constants cannot have `[const]` trait bounds
.object = trait objects cannot have `[const]` trait bounds
.item = this item cannot have `[const]` trait bounds

View file

@ -22,20 +22,19 @@ use std::str::FromStr;
use itertools::{Either, Itertools};
use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
use rustc_ast::ptr::P;
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;
@ -146,25 +145,24 @@ impl<'a> AstValidator<'a> {
&mut self,
ty_alias: &TyAlias,
) -> Result<(), errors::WhereClauseBeforeTypeAlias> {
if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token {
if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token {
return Ok(());
}
let (before_predicates, after_predicates) =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split);
let span = ty_alias.where_clauses.before.span;
let span = ty_alias.generics.where_clause.span;
let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token
let sugg = if !ty_alias.generics.where_clause.predicates.is_empty()
|| !ty_alias.after_where_clause.has_where_token
{
let mut state = State::new();
if !ty_alias.where_clauses.after.has_where_token {
if !ty_alias.after_where_clause.has_where_token {
state.space();
state.word_space("where");
}
let mut first = after_predicates.is_empty();
for p in before_predicates {
let mut first = ty_alias.after_where_clause.predicates.is_empty();
for p in &ty_alias.generics.where_clause.predicates {
if !first {
state.word_space(",");
}
@ -175,7 +173,7 @@ impl<'a> AstValidator<'a> {
errors::WhereClauseBeforeTypeAliasSugg::Move {
left: span,
snippet: state.s.eof(),
right: ty_alias.where_clauses.after.span.shrink_to_hi(),
right: ty_alias.after_where_clause.span.shrink_to_hi(),
}
} else {
errors::WhereClauseBeforeTypeAliasSugg::Remove { span }
@ -294,6 +292,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);
@ -391,7 +404,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,
@ -450,12 +480,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 { " " };
@ -529,11 +565,7 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::BoundInContext { span, ctx });
}
fn check_foreign_ty_genericless(
&self,
generics: &Generics,
where_clauses: &TyAliasWhereClauses,
) {
fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) {
let cannot_have = |span, descr, remove_descr| {
self.dcx().emit_err(errors::ExternTypesCannotHave {
span,
@ -547,14 +579,14 @@ impl<'a> AstValidator<'a> {
cannot_have(generics.span, "generic parameters", "generic parameters");
}
let check_where_clause = |where_clause: TyAliasWhereClause| {
let check_where_clause = |where_clause: &WhereClause| {
if where_clause.has_where_token {
cannot_have(where_clause.span, "`where` clauses", "`where` clause");
}
};
check_where_clause(where_clauses.before);
check_where_clause(where_clauses.after);
check_where_clause(&generics.where_clause);
check_where_clause(&after_where_clause);
}
fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
@ -628,46 +660,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) {
@ -699,23 +753,27 @@ impl<'a> AstValidator<'a> {
}
}
fn deny_super_traits(&self, bounds: &GenericBounds, ident_span: Span) {
fn deny_super_traits(&self, bounds: &GenericBounds, ident: Span) {
if let [.., last] = &bounds[..] {
let span = ident_span.shrink_to_hi().to(last.span());
self.dcx().emit_err(errors::AutoTraitBounds { span, ident: ident_span });
let span = bounds.iter().map(|b| b.span()).collect();
let removal = ident.shrink_to_hi().to(last.span());
self.dcx().emit_err(errors::AutoTraitBounds { span, removal, ident });
}
}
fn deny_where_clause(&self, where_clause: &WhereClause, ident_span: Span) {
fn deny_where_clause(&self, where_clause: &WhereClause, ident: Span) {
if !where_clause.predicates.is_empty() {
// FIXME: The current diagnostic is misleading since it only talks about
// super trait and lifetime bounds while we should just say “bounds”.
self.dcx()
.emit_err(errors::AutoTraitBounds { span: where_clause.span, ident: ident_span });
self.dcx().emit_err(errors::AutoTraitBounds {
span: vec![where_clause.span],
removal: where_clause.span,
ident,
});
}
}
fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
fn deny_items(&self, trait_items: &[Box<AssocItem>], ident_span: Span) {
if !trait_items.is_empty() {
let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect();
let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
@ -835,7 +893,7 @@ impl<'a> AstValidator<'a> {
MISSING_ABI,
id,
span,
BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
)
}
}
@ -951,13 +1009,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,
}) => {
@ -989,46 +1050,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)
@ -1094,7 +1121,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(),
},
);
@ -1124,7 +1151,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
);
}
}
visit::walk_item(self, item)
self.with_tilde_const(Some(TildeConstReason::Enum { span: item.span }), |this| {
visit::walk_item(this, item)
});
}
ItemKind::Trait(box Trait {
constness,
@ -1163,38 +1192,52 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
});
}
ItemKind::TraitAlias(box TraitAlias { constness, generics, bounds, .. }) => {
let disallowed = matches!(constness, ast::Const::No)
.then(|| TildeConstReason::Trait { span: item.span });
self.with_tilde_const(disallowed, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
}
ItemKind::Mod(safety, ident, mod_kind) => {
if let &Safety::Unsafe(span) = safety {
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);
}
visit::walk_item(self, item)
}
ItemKind::Struct(ident, generics, vdata) => match vdata {
VariantData::Struct { fields, .. } => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.visit_generics(generics);
walk_list!(self, visit_field_def, fields);
}
_ => visit::walk_item(self, item),
},
ItemKind::Struct(ident, generics, vdata) => {
self.with_tilde_const(Some(TildeConstReason::Struct { span: item.span }), |this| {
match vdata {
VariantData::Struct { fields, .. } => {
this.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
this.visit_generics(generics);
walk_list!(this, visit_field_def, fields);
}
_ => visit::walk_item(this, item),
}
})
}
ItemKind::Union(ident, generics, vdata) => {
if vdata.fields().is_empty() {
self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
}
match vdata {
VariantData::Struct { fields, .. } => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.visit_generics(generics);
walk_list!(self, visit_field_def, fields);
self.with_tilde_const(Some(TildeConstReason::Union { span: item.span }), |this| {
match vdata {
VariantData::Struct { fields, .. } => {
this.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
this.visit_generics(generics);
walk_list!(this, visit_field_def, fields);
}
_ => visit::walk_item(this, item),
}
_ => visit::walk_item(self, item),
}
});
}
ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
self.check_defaultness(item.span, *defaultness);
@ -1221,7 +1264,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_item(self, item);
}
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
@ -1236,9 +1279,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
self.dcx().emit_err(err);
}
} else if where_clauses.after.has_where_token {
} else if after_where_clause.has_where_token {
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.after.span,
span: after_where_clause.span,
help: self.sess.is_nightly_build(),
});
}
@ -1268,7 +1311,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
..
@ -1276,7 +1319,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, where_clauses);
self.check_foreign_ty_genericless(generics, after_where_clause);
self.check_foreign_item_ascii_only(*ident);
}
ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => {
@ -1586,6 +1629,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);
}
}
@ -1623,6 +1667,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
_ => self.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)),
}
}
fn visit_anon_const(&mut self, anon_const: &'a AnonConst) {
self.with_tilde_const(
Some(TildeConstReason::AnonConst { span: anon_const.value.span }),
|this| visit::walk_anon_const(this, anon_const),
)
}
}
/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
@ -1754,7 +1805,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);
}

View file

@ -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)]
@ -344,7 +381,7 @@ pub(crate) struct ModuleNonAscii {
#[diag(ast_passes_auto_generic, code = E0567)]
pub(crate) struct AutoTraitGeneric {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub span: Span,
#[label]
pub ident: Span,
@ -354,8 +391,9 @@ pub(crate) struct AutoTraitGeneric {
#[diag(ast_passes_auto_super_lifetime, code = E0568)]
pub(crate) struct AutoTraitBounds {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
pub span: Span,
pub span: Vec<Span>,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub removal: Span,
#[label]
pub ident: Span,
}
@ -365,7 +403,7 @@ pub(crate) struct AutoTraitBounds {
pub(crate) struct AutoTraitItems {
#[primary_span]
pub spans: Vec<Span>,
#[suggestion(code = "", applicability = "machine-applicable")]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub total: Span,
#[label]
pub ident: Span,
@ -463,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 {
@ -504,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 {
@ -623,6 +642,26 @@ pub(crate) enum TildeConstReason {
#[primary_span]
span: Span,
},
#[note(ast_passes_struct)]
Struct {
#[primary_span]
span: Span,
},
#[note(ast_passes_enum)]
Enum {
#[primary_span]
span: Span,
},
#[note(ast_passes_union)]
Union {
#[primary_span]
span: Span,
},
#[note(ast_passes_anon_const)]
AnonConst {
#[primary_span]
span: Span,
},
#[note(ast_passes_object)]
TraitObject,
#[note(ast_passes_item)]
@ -651,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)]
@ -810,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 {
@ -886,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,
}

View file

@ -183,11 +183,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_doc!(
"experimental" {
cfg => doc_cfg
cfg_hide => doc_cfg_hide
auto_cfg => doc_cfg
masked => doc_masked
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) {
@ -620,11 +622,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
}
fn check_incompatible_features(sess: &Session, features: &Features) {
let enabled_lang_features =
features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
let enabled_lib_features =
features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
let enabled_features = enabled_lang_features.chain(enabled_lib_features);
let enabled_features = features.enabled_features_iter_stable_order();
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
.iter()

View file

@ -298,7 +298,7 @@ impl Printer {
}
}
// This is is where `BoxMarker`s are produced.
// This is where `BoxMarker`s are produced.
fn scan_begin(&mut self, token: BeginToken) -> BoxMarker {
if self.scan_stack.is_empty() {
self.left_total = 1;
@ -310,7 +310,7 @@ impl Printer {
BoxMarker
}
// This is is where `BoxMarker`s are consumed.
// This is where `BoxMarker`s are consumed.
fn scan_end(&mut self, b: BoxMarker) {
if self.scan_stack.is_empty() {
self.print_end();

View file

@ -10,8 +10,7 @@ use std::borrow::Cow;
use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::ptr::P;
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};
@ -442,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)
}
@ -1016,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 */
@ -1178,7 +1176,7 @@ impl<'a> State<'a> {
self.end(rb);
}
fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
fn commasep_exprs(&mut self, b: Breaks, exprs: &[Box<ast::Expr>]) {
self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e, FixupContext::default()), |e| e.span)
}
@ -1234,6 +1232,7 @@ impl<'a> State<'a> {
self.print_expr_anon_const(end, &[]);
}
}
rustc_ast::TyPatKind::NotNull => self.word("!null"),
rustc_ast::TyPatKind::Or(variants) => {
let mut first = true;
for pat in variants {
@ -1713,10 +1712,15 @@ impl<'a> State<'a> {
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
self.word_nbsp("ref");
if pinnedness.is_pinned() {
self.word_nbsp("pin");
}
if rmutbl.is_mut() {
self.word_nbsp("mut");
} else if pinnedness.is_pinned() {
self.word_nbsp("const");
}
}
self.print_ident(*ident);
@ -1771,7 +1775,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(",");
}

View file

@ -2,7 +2,6 @@ use std::fmt::Write;
use ast::{ForLoopKind, MatchKind};
use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
@ -54,7 +53,7 @@ impl<'a> State<'a> {
self.print_else(elseopt)
}
fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
fn print_call_post(&mut self, args: &[Box<ast::Expr>]) {
self.popen();
self.commasep_exprs(Inconsistent, args);
self.pclose()
@ -111,7 +110,7 @@ impl<'a> State<'a> {
}
}
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) {
fn print_expr_vec(&mut self, exprs: &[Box<ast::Expr>]) {
let ib = self.ibox(INDENT_UNIT);
self.word("[");
self.commasep_exprs(Inconsistent, exprs);
@ -149,7 +148,7 @@ impl<'a> State<'a> {
fn print_expr_struct(
&mut self,
qself: &Option<P<ast::QSelf>>,
qself: &Option<Box<ast::QSelf>>,
path: &ast::Path,
fields: &[ast::ExprField],
rest: &ast::StructRest,
@ -204,7 +203,7 @@ impl<'a> State<'a> {
self.word("}");
}
fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>]) {
fn print_expr_tup(&mut self, exprs: &[Box<ast::Expr>]) {
self.popen();
self.commasep_exprs(Inconsistent, exprs);
if exprs.len() == 1 {
@ -213,7 +212,7 @@ impl<'a> State<'a> {
self.pclose()
}
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
fn print_expr_call(&mut self, func: &ast::Expr, args: &[Box<ast::Expr>], fixup: FixupContext) {
// Independent of parenthesization related to precedence, we must
// parenthesize `func` if this is a statement context in which without
// parentheses, a statement boundary would occur inside `func` or
@ -247,7 +246,7 @@ impl<'a> State<'a> {
&mut self,
segment: &ast::PathSegment,
receiver: &ast::Expr,
base_args: &[P<ast::Expr>],
base_args: &[Box<ast::Expr>],
fixup: FixupContext,
) {
// The fixup here is different than in `print_expr_call` because

View file

@ -1,8 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast as ast;
use rustc_ast::ModKind;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, ModKind, TraitAlias};
use rustc_span::Ident;
use crate::pp::BoxMarker;
@ -60,14 +58,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -128,14 +126,12 @@ impl<'a> State<'a> {
&mut self,
ident: Ident,
generics: &ast::Generics,
where_clauses: ast::TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
bounds: &ast::GenericBounds,
ty: Option<&ast::Ty>,
vis: &ast::Visibility,
defaultness: ast::Defaultness,
) {
let (before_predicates, after_predicates) =
generics.where_clause.predicates.split_at(where_clauses.split);
let (cb, ib) = self.head("");
self.print_visibility(vis);
self.print_defaultness(defaultness);
@ -146,13 +142,13 @@ impl<'a> State<'a> {
self.word_nbsp(":");
self.print_type_bounds(bounds);
}
self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates);
self.print_where_clause(&generics.where_clause);
if let Some(ty) = ty {
self.space();
self.word_space("=");
self.print_type(ty);
}
self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates);
self.print_where_clause(&after_where_clause);
self.word(";");
self.end(ib);
self.end(cb);
@ -284,14 +280,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
&item.vis,
@ -309,39 +305,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);
@ -387,8 +385,11 @@ impl<'a> State<'a> {
let empty = item.attrs.is_empty() && items.is_empty();
self.bclose(item.span, empty, cb);
}
ast::ItemKind::TraitAlias(ident, generics, bounds) => {
let (cb, ib) = self.head(visibility_qualified(&item.vis, "trait"));
ast::ItemKind::TraitAlias(box TraitAlias { constness, ident, generics, bounds }) => {
let (cb, ib) = self.head("");
self.print_visibility(&item.vis);
self.print_constness(*constness);
self.word_nbsp("trait");
self.print_ident(*ident);
self.print_generic_params(&generics.params);
self.nbsp();
@ -584,14 +585,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -628,10 +629,10 @@ impl<'a> State<'a> {
&mut self,
attrs: &[ast::Attribute],
vis: &ast::Visibility,
qself: &Option<P<ast::QSelf>>,
qself: &Option<Box<ast::QSelf>>,
path: &ast::Path,
kind: DelegationKind<'_>,
body: &Option<P<ast::Block>>,
body: &Option<Box<ast::Block>>,
) {
let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
self.print_visibility(vis);
@ -758,14 +759,7 @@ impl<'a> State<'a> {
}
fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates);
}
fn print_where_clause_parts(
&mut self,
has_where_token: bool,
predicates: &[ast::WherePredicate],
) {
let ast::WhereClause { has_where_token, ref predicates, span: _ } = *where_clause;
if predicates.is_empty() && !has_where_token {
return;
}

View file

@ -1,16 +0,0 @@
[package]
name = "rustc_attr_data_structures"
version = "0.0.0"
edition = "2024"
[dependencies]
# tidy-alphabetical-start
rustc_abi = {path = "../rustc_abi"}
rustc_ast = {path = "../rustc_ast"}
rustc_ast_pretty = {path = "../rustc_ast_pretty"}
rustc_data_structures = {path = "../rustc_data_structures"}
rustc_macros = {path = "../rustc_macros"}
rustc_serialize = {path = "../rustc_serialize"}
rustc_span = {path = "../rustc_span"}
thin-vec = "0.2.12"
# tidy-alphabetical-end

View file

@ -1,472 +0,0 @@
use rustc_abi::Align;
use rustc_ast::token::CommentKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::hygiene::Transparency;
use rustc_span::{Ident, Span, Symbol};
use thin_vec::ThinVec;
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
pub enum InlineAttr {
None,
Hint,
Always,
Never,
/// `#[rustc_force_inline]` forces inlining to happen in the MIR inliner - it reports an error
/// if the inlining cannot happen. It is limited to only free functions so that the calls
/// can always be resolved.
Force {
attr_span: Span,
reason: Option<Symbol>,
},
}
impl InlineAttr {
pub fn always(&self) -> bool {
match self {
InlineAttr::Always | InlineAttr::Force { .. } => true,
InlineAttr::None | InlineAttr::Hint | InlineAttr::Never => false,
}
}
}
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
pub enum InstructionSetAttr {
ArmA32,
ArmT32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum OptimizeAttr {
/// No `#[optimize(..)]` attribute
#[default]
Default,
/// `#[optimize(none)]`
DoNotOptimize,
/// `#[optimize(speed)]`
Speed,
/// `#[optimize(size)]`
Size,
}
impl OptimizeAttr {
pub fn do_not_optimize(&self) -> bool {
matches!(self, Self::DoNotOptimize)
}
}
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
pub enum ReprAttr {
ReprInt(IntType),
ReprRust,
ReprC,
ReprPacked(Align),
ReprSimd,
ReprTransparent,
ReprAlign(Align),
}
pub use ReprAttr::*;
use rustc_span::def_id::DefId;
pub enum TransparencyError {
UnknownTransparency(Symbol, Span),
MultipleTransparencyAttrs(Span, Span),
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
pub enum IntType {
SignedInt(ast::IntTy),
UnsignedInt(ast::UintTy),
}
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub struct Deprecation {
pub since: DeprecatedSince,
/// The note to issue a reason.
pub note: Option<Symbol>,
/// A text snippet used to completely replace any use of the deprecated item in an expression.
///
/// This is currently unstable.
pub suggestion: Option<Symbol>,
}
/// Release in which an API is deprecated.
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub enum DeprecatedSince {
RustcVersion(RustcVersion),
/// Deprecated in the future ("to be determined").
Future,
/// `feature(staged_api)` is off. Deprecation versions outside the standard
/// library are allowed to be arbitrary strings, for better or worse.
NonStandard(Symbol),
/// Deprecation version is unspecified but optional.
Unspecified,
/// Failed to parse a deprecation version, or the deprecation version is
/// unspecified and required. An error has already been emitted.
Err,
}
/// Successfully-parsed value of a `#[coverage(..)]` attribute.
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum CoverageAttrKind {
On,
Off,
}
impl Deprecation {
/// Whether an item marked with #[deprecated(since = "X")] is currently
/// deprecated (i.e., whether X is not greater than the current rustc
/// version).
pub fn is_in_effect(&self) -> bool {
match self.since {
DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
DeprecatedSince::Future => false,
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
DeprecatedSince::NonStandard(_) => true,
// Assume deprecation is in effect if "since" field is absent or invalid.
DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
}
}
pub fn is_since_rustc_version(&self) -> bool {
matches!(self.since, DeprecatedSince::RustcVersion(_))
}
}
/// There are three valid forms of the attribute:
/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable.
/// `#[used(compiler)]`
/// `#[used(linker)]`
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum UsedBy {
Compiler,
Linker,
}
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum MacroUseArgs {
UseAll,
UseSpecific(ThinVec<Ident>),
}
impl Default for MacroUseArgs {
fn default() -> Self {
Self::UseSpecific(ThinVec::new())
}
}
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
pub struct StrippedCfgItem<ModId = DefId> {
pub parent_module: ModId,
pub ident: Ident,
pub cfg: (CfgEntry, Span),
}
impl<ModId> StrippedCfgItem<ModId> {
pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
}
}
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum CfgEntry {
All(ThinVec<CfgEntry>, Span),
Any(ThinVec<CfgEntry>, Span),
Not(Box<CfgEntry>, Span),
Bool(bool, Span),
NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span },
Version(Option<RustcVersion>, Span),
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
/// These attributes are markers that guide the compilation process and are never expanded into other code.
/// They persist throughout the compilation phases, from AST to HIR and beyond.
///
/// ## Attribute Processing
/// While attributes are initially parsed by [`rustc_parse`] into [`ast::Attribute`], they still contain raw token streams
/// because different attributes have different internal structures. This enum represents the final,
/// fully parsed form of these attributes, where each variant contains all the information and
/// structure relevant for the specific attribute.
///
/// Some attributes can be applied multiple times to the same item, and they are "collapsed" into a single
/// semantic attribute. For example:
/// ```rust
/// #[repr(C)]
/// #[repr(packed)]
/// struct S { }
/// ```
/// This is equivalent to `#[repr(C, packed)]` and results in a single [`AttributeKind::Repr`] containing
/// both `C` and `packed` annotations. This collapsing happens during parsing and is reflected in the
/// data structures defined in this enum.
///
/// ## Usage
/// These parsed attributes are used throughout the compiler to:
/// - Control code generation (e.g., `#[repr]`)
/// - Mark API stability (`#[stable]`, `#[unstable]`)
/// - Provide documentation (`#[doc]`)
/// - Guide compiler behavior (e.g., `#[allow_internal_unstable]`)
///
/// ## Note on Attribute Organization
/// Some attributes like `InlineAttr`, `OptimizeAttr`, and `InstructionSetAttr` are defined separately
/// from this enum because they are used in specific compiler phases (like code generation) and don't
/// need to persist throughout the entire compilation process. They are typically processed and
/// converted into their final form earlier in the compilation pipeline.
///
/// For example:
/// - `InlineAttr` is used during code generation to control function inlining
/// - `OptimizeAttr` is used to control optimization levels
/// - `InstructionSetAttr` is used for target-specific code generation
///
/// These attributes are handled by their respective compiler passes in the [`rustc_codegen_ssa`] crate
/// and don't need to be preserved in the same way as the attributes in this enum.
///
/// For more details on attribute parsing, see the [`rustc_attr_parsing`] crate.
///
/// [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
/// [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum AttributeKind {
// tidy-alphabetical-start
/// Represents `#[align(N)]`.
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
Align { align: Align, span: Span },
/// Represents `#[rustc_allow_const_fn_unstable]`.
AllowConstFnUnstable(ThinVec<Symbol>, Span),
/// Represents `#[rustc_allow_incoherent_impl]`.
AllowIncoherentImpl(Span),
/// Represents `#[allow_internal_unstable]`.
AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
AsPtr(Span),
/// Represents `#[automatically_derived]`
AutomaticallyDerived(Span),
/// Represents `#[rustc_default_body_unstable]`.
BodyStability {
stability: DefaultBodyStability,
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
span: Span,
},
/// Represents `#[rustc_coherence_is_core]`.
CoherenceIsCore,
/// Represents `#[rustc_coinductive]`.
Coinductive(Span),
/// Represents `#[cold]`.
Cold(Span),
/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
// FIXME(jdonszelmann): remove when target validation code is moved
first_span: Span,
},
/// Represents `#[const_continue]`.
ConstContinue(Span),
/// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
ConstStability {
stability: PartialConstStability,
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
span: Span,
},
/// Represents `#[rustc_const_stable_indirect]`.
ConstStabilityIndirect,
/// Represents `#[const_trait]`.
ConstTrait(Span),
/// Represents `#[coverage(..)]`.
Coverage(Span, CoverageAttrKind),
///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
/// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
Deprecation { deprecation: Deprecation, span: Span },
/// Represents `#[rustc_do_not_implement_via_object]`.
DoNotImplementViaObject(Span),
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
/// Represents `#[rustc_dummy]`.
Dummy,
/// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute).
ExportName {
/// The name to export this item with.
/// It may not contain \0 bytes as it will be converted to a null-terminated string.
name: Symbol,
span: Span,
},
/// Represents `#[export_stable]`.
ExportStable,
/// Represents `#[ffi_const]`.
FfiConst(Span),
/// Represents `#[ffi_pure]`.
FfiPure(Span),
/// Represents `#[fundamental]`.
Fundamental,
/// Represents `#[ignore]`
Ignore {
span: Span,
/// ignore can optionally have a reason: `#[ignore = "reason this is ignored"]`
reason: Option<Symbol>,
},
/// Represents `#[inline]` and `#[rustc_force_inline]`.
Inline(InlineAttr, Span),
/// Represents `#[link_name]`.
LinkName { name: Symbol, span: Span },
/// Represents `#[link_ordinal]`.
LinkOrdinal { ordinal: u16, span: Span },
/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
LinkSection { name: Symbol, span: Span },
/// Represents `#[loop_match]`.
LoopMatch(Span),
/// Represents `#[macro_escape]`.
MacroEscape(Span),
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),
/// Represents `#[macro_use]`.
MacroUse { span: Span, arguments: MacroUseArgs },
/// Represents `#[marker]`.
Marker(Span),
/// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
MayDangle(Span),
/// Represents `#[must_use]`.
MustUse {
span: Span,
/// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
reason: Option<Symbol>,
},
/// Represents `#[naked]`
Naked(Span),
/// Represents `#[no_implicit_prelude]`
NoImplicitPrelude(Span),
/// Represents `#[no_mangle]`
NoMangle(Span),
/// Represents `#[non_exhaustive]`
NonExhaustive(Span),
/// Represents `#[omit_gdb_pretty_printer_section]`
OmitGdbPrettyPrinterSection,
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
/// Represents `#[rustc_paren_sugar]`.
ParenSugar(Span),
/// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
PassByValue(Span),
/// Represents `#[path]`
Path(Symbol, Span),
/// Represents `#[pointee]`
Pointee(Span),
/// Represents `#[proc_macro]`
ProcMacro(Span),
/// Represents `#[proc_macro_attribute]`
ProcMacroAttribute(Span),
/// Represents `#[proc_macro_derive]`
ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
PubTransparent(Span),
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
/// Represents `#[rustc_builtin_macro]`.
RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_layout_scalar_valid_range_end]`.
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
/// Represents `#[rustc_layout_scalar_valid_range_start]`.
RustcLayoutScalarValidRangeStart(Box<u128>, Span),
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_skip_during_method_dispatch]`.
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
/// Represents `#[rustc_specialization_trait]`.
SpecializationTrait(Span),
/// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
Stability {
stability: Stability,
/// Span of the attribute.
span: Span,
},
/// Represents `#[rustc_std_internal_symbol]`.
StdInternalSymbol(Span),
/// Represents `#[target_feature(enable = "...")]`
TargetFeature(ThinVec<(Symbol, Span)>, Span),
/// Represents `#[track_caller]`
TrackCaller(Span),
/// Represents `#[type_const]`.
TypeConst(Span),
/// Represents `#[rustc_unsafe_specialization_marker]`.
UnsafeSpecializationMarker(Span),
/// Represents `#[unstable_feature_bound]`.
UnstableFeatureBound(ThinVec<(Symbol, Span)>),
/// Represents `#[used]`
Used { used_by: UsedBy, span: Span },
// tidy-alphabetical-end
}

View file

@ -1,86 +0,0 @@
use crate::AttributeKind;
#[derive(PartialEq)]
pub enum EncodeCrossCrate {
Yes,
No,
}
impl AttributeKind {
pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
use AttributeKind::*;
use EncodeCrossCrate::*;
match self {
// tidy-alphabetical-start
Align { .. } => No,
AllowConstFnUnstable(..) => No,
AllowIncoherentImpl(..) => No,
AllowInternalUnstable(..) => Yes,
AsPtr(..) => Yes,
AutomaticallyDerived(..) => Yes,
BodyStability { .. } => No,
CoherenceIsCore => No,
Coinductive(..) => No,
Cold(..) => No,
Confusables { .. } => Yes,
ConstContinue(..) => No,
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
Coverage(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
DocComment { .. } => Yes,
Dummy => No,
ExportName { .. } => Yes,
ExportStable => No,
FfiConst(..) => No,
FfiPure(..) => No,
Fundamental { .. } => Yes,
Ignore { .. } => No,
Inline(..) => No,
LinkName { .. } => Yes, // Needed for rustdoc
LinkOrdinal { .. } => No,
LinkSection { .. } => Yes, // Needed for rustdoc
LoopMatch(..) => No,
MacroEscape(..) => No,
MacroTransparency(..) => Yes,
MacroUse { .. } => No,
Marker(..) => No,
MayDangle(..) => No,
MustUse { .. } => Yes,
Naked(..) => No,
NoImplicitPrelude(..) => No,
NoMangle(..) => Yes, // Needed for rustdoc
NonExhaustive(..) => Yes, // Needed for rustdoc
OmitGdbPrettyPrinterSection => No,
Optimize(..) => No,
ParenSugar(..) => No,
PassByValue(..) => Yes,
Path(..) => No,
Pointee(..) => No,
ProcMacro(..) => No,
ProcMacroAttribute(..) => No,
ProcMacroDerive { .. } => No,
PubTransparent(..) => Yes,
Repr { .. } => No,
RustcBuiltinMacro { .. } => Yes,
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
SkipDuringMethodDispatch { .. } => No,
SpecializationTrait(..) => No,
Stability { .. } => Yes,
StdInternalSymbol(..) => No,
TargetFeature(..) => No,
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
UnsafeSpecializationMarker(..) => No,
UnstableFeatureBound(..) => No,
Used { .. } => No,
// tidy-alphabetical-end
}
}
}

View file

@ -1,216 +0,0 @@
//! Data structures for representing parsed attributes in the Rust compiler.
//! For detailed documentation about attribute processing,
//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html).
// tidy-alphabetical-start
#![allow(internal_features)]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
// tidy-alphabetical-end
mod attributes;
mod encode_cross_crate;
mod stability;
mod version;
pub mod lints;
use std::num::NonZero;
pub use attributes::*;
pub use encode_cross_crate::EncodeCrossCrate;
use rustc_abi::Align;
use rustc_ast::token::CommentKind;
use rustc_ast::{AttrStyle, IntTy, UintTy};
use rustc_ast_pretty::pp::Printer;
use rustc_span::hygiene::Transparency;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
pub use stability::*;
use thin_vec::ThinVec;
pub use version::*;
/// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro
/// instead of implementing everything in `rustc_middle`.
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
/// This trait is used to print attributes in `rustc_hir_pretty`.
///
/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
/// The output will look a lot like a `Debug` implementation, but fields of several types
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
/// representation much.
pub trait PrintAttribute {
/// Whether or not this will render as something meaningful, or if it's skipped
/// (which will force the containing struct to also skip printing a comma
/// and the field name).
fn should_render(&self) -> bool;
fn print_attribute(&self, p: &mut Printer);
}
impl PrintAttribute for u128 {
fn should_render(&self) -> bool {
true
}
fn print_attribute(&self, p: &mut Printer) {
p.word(self.to_string())
}
}
impl<T: PrintAttribute> PrintAttribute for &T {
fn should_render(&self) -> bool {
T::should_render(self)
}
fn print_attribute(&self, p: &mut Printer) {
T::print_attribute(self, p)
}
}
impl<T: PrintAttribute> PrintAttribute for Option<T> {
fn should_render(&self) -> bool {
self.as_ref().is_some_and(|x| x.should_render())
}
fn print_attribute(&self, p: &mut Printer) {
if let Some(i) = self {
T::print_attribute(i, p)
}
}
}
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
fn should_render(&self) -> bool {
self.is_empty() || self[0].should_render()
}
fn print_attribute(&self, p: &mut Printer) {
let mut last_printed = false;
p.word("[");
for i in self {
if last_printed {
p.word_space(",");
}
i.print_attribute(p);
last_printed = i.should_render();
}
p.word("]");
}
}
macro_rules! print_skip {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn should_render(&self) -> bool { false }
fn print_attribute(&self, _: &mut Printer) { }
})*
};
}
macro_rules! print_disp {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn should_render(&self) -> bool { true }
fn print_attribute(&self, p: &mut Printer) {
p.word(format!("{}", self));
}
}
)*};
}
macro_rules! print_debug {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn should_render(&self) -> bool { true }
fn print_attribute(&self, p: &mut Printer) {
p.word(format!("{:?}", self));
}
}
)*};
}
macro_rules! print_tup {
(num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
() => {};
($t: ident $($ts: ident)*) => {
#[allow(non_snake_case, unused)]
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
fn should_render(&self) -> bool {
let ($t, $($ts),*) = self;
print_tup!(num_should_render $t $($ts)*) != 0
}
fn print_attribute(&self, p: &mut Printer) {
let ($t, $($ts),*) = self;
let parens = print_tup!(num_should_render $t $($ts)*) > 1;
if parens {
p.popen();
}
let mut printed_anything = $t.should_render();
$t.print_attribute(p);
$(
if $ts.should_render() {
if printed_anything {
p.word_space(",");
}
printed_anything = true;
}
$ts.print_attribute(p);
)*
if parens {
p.pclose();
}
}
}
print_tup!($($ts)*);
};
}
print_tup!(A B C D E F G H);
print_skip!(Span, (), ErrorGuaranteed);
print_disp!(u16, bool, NonZero<u32>);
print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
'done: {
for i in $attributes_list {
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
break 'done Some($e);
}
_ => {}
}
}
None
}
}};
}

View file

@ -1,16 +0,0 @@
use rustc_macros::HashStable_Generic;
use rustc_span::Span;
#[derive(Clone, Debug, HashStable_Generic)]
pub struct AttributeLint<Id> {
pub id: Id,
pub span: Span,
pub kind: AttributeLintKind,
}
#[derive(Clone, Debug, HashStable_Generic)]
pub enum AttributeLintKind {
UnusedDuplicate { this: Span, other: Span, warning: bool },
IllFormedAttributeInput { suggestions: Vec<String> },
EmptyAttribute { first_span: Span },
}

View file

@ -1,47 +0,0 @@
use std::fmt::{self, Display};
use std::sync::OnceLock;
use rustc_macros::{
Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
};
use crate::PrintAttribute;
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub struct RustcVersion {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
impl RustcVersion {
pub const CURRENT: Self = current_rustc_version!();
pub fn current_overridable() -> Self {
*CURRENT_OVERRIDABLE.get_or_init(|| {
if let Ok(override_var) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING")
&& let Some(override_) = Self::parse_str(&override_var)
{
override_
} else {
Self::CURRENT
}
})
}
fn parse_str(value: &str) -> Option<Self> {
// Ignore any suffixes such as "-dev" or "-nightly".
let mut components = value.split('-').next().unwrap().splitn(3, '.');
let major = components.next()?.parse().ok()?;
let minor = components.next()?.parse().ok()?;
let patch = components.next().unwrap_or("0").parse().ok()?;
Some(RustcVersion { major, minor, patch })
}
}
static CURRENT_OVERRIDABLE: OnceLock<RustcVersion> = OnceLock::new();
impl Display for RustcVersion {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
}
}

View file

@ -8,14 +8,15 @@ edition = "2024"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
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

View file

@ -1,3 +1,11 @@
attr_parsing_as_needed_compatibility =
linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters
attr_parsing_cfg_predicate_identifier =
`cfg` predicate key must be an identifier
@ -8,10 +16,22 @@ attr_parsing_deprecated_item_suggestion =
attr_parsing_empty_attribute =
unused attribute
.suggestion = remove this attribute
.suggestion = {$valid_without_list ->
[true] remove these parentheses
*[other] remove this attribute
}
.note = {$valid_without_list ->
[true] using `{$attr_path}` with an empty list is equivalent to not using a list at all
*[other] using `{$attr_path}` with an empty list has no effect
}
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_empty_link_name =
link name must not be empty
.label = empty link name
attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern
@ -32,6 +52,15 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
*[other] valid forms for the attribute are {$suggestions}
}
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_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@ -51,6 +80,11 @@ attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
attr_parsing_invalid_alignment_value =
invalid alignment value: {$error_part}
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_issue_string =
`issue` must be a non-zero numeric string or "none"
.must_not_be_zero = `issue` must not be "0", use "none" instead
@ -59,6 +93,13 @@ attr_parsing_invalid_issue_string =
.pos_overflow = number too large to fit in target type
.neg_overflow = number too small to fit in target type
attr_parsing_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
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_invalid_predicate =
invalid predicate `{$predicate}`
@ -78,9 +119,42 @@ 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_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_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}
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_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
.note = the value may not exceed `u16::MAX`
attr_parsing_link_requires_name =
`#[link]` attribute requires a `name = "string"` argument
.label = missing `name` argument
attr_parsing_meta_bad_delim = wrong meta list delimiters
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
attr_parsing_missing_feature =
missing 'feature'
@ -93,6 +167,9 @@ attr_parsing_missing_note =
attr_parsing_missing_since =
missing 'since'
attr_parsing_multiple_modifiers =
multiple `{$modifier}` modifiers in a single `modifiers` argument
attr_parsing_multiple_stability_levels =
multiple stability levels
@ -108,6 +185,23 @@ 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_raw_dylib_elf_unstable =
link kind `raw-dylib` is unstable on ELF platforms
attr_parsing_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
attr_parsing_raw_dylib_only_windows =
link kind `raw-dylib` is only supported on Windows targets
attr_parsing_repr_ident =
meta item in `repr` must be an identifier
@ -122,6 +216,9 @@ attr_parsing_soft_no_args =
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
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_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}
@ -132,11 +229,16 @@ 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_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_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 +263,6 @@ 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_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind

View file

@ -1,12 +1,6 @@
use std::iter;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
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>,

View file

@ -0,0 +1,12 @@
//! Attributes that can be found in function body.
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);
}

View file

@ -1,23 +1,41 @@
use rustc_ast::{LitKind, NodeId};
use rustc_attr_data_structures::{CfgEntry, RustcVersion};
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttributeTemplate, Features, template};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, RustcVersion};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use crate::context::{AcceptContext, ShouldEmit, Stage};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
};
use crate::{
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
AttributeParser, 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>(
const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
List: &["predicate, attr1, attr2, ..."],
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
);
pub fn parse_cfg<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> Option<CfgEntry> {
@ -32,7 +50,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> {
@ -66,9 +84,7 @@ fn parse_cfg_entry<S: Stage>(
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: meta.path().span(),
});
cx.expected_identifier(meta.path().span());
return None;
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
@ -77,7 +93,7 @@ fn parse_cfg_entry<S: Stage>(
MetaItemOrLitParser::Lit(lit) => match lit.kind {
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span });
cx.expected_identifier(lit.span);
return None;
}
},
@ -145,9 +161,7 @@ fn parse_cfg_entry_target<S: Stage>(
// Then, parse it as a name-value item
let Some(name) = sub_item.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: sub_item.path().span(),
});
cx.expected_identifier(sub_item.path().span());
return None;
};
let name = Symbol::intern(&format!("target_{name}"));
@ -296,3 +310,122 @@ impl EvalConfigResult {
}
}
}
pub fn parse_cfg_attr(
cfg_attr: &Attribute,
sess: &Session,
features: Option<&Features>,
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
match cfg_attr.get_normal_item().args {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
check_cfg_attr_bad_delim(&sess.psess, dspan, delim);
match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| {
parse_cfg_attr_internal(p, sess, features, cfg_attr)
}) {
Ok(r) => return Some(r),
Err(e) => {
let suggestions =
CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr);
e.with_span_suggestions(
cfg_attr.span,
"must be of the form",
suggestions,
Applicability::HasPlaceholders,
)
.with_note(format!(
"for more information, visit <{}>",
CFG_ATTR_TEMPLATE.docs.expect("cfg_attr has docs")
))
.emit();
}
}
}
_ => {
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
cfg_attr.get_normal_item().args
{
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
} else {
(cfg_attr.span, AttributeParseErrorReason::ExpectedList)
};
sess.dcx().emit_err(AttributeParseError {
span,
attr_span: cfg_attr.span,
template: CFG_ATTR_TEMPLATE,
attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
reason,
suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr),
});
}
}
None
}
fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(CfgAttrBadDelim {
span: span.entire(),
sugg: MetaBadDelimSugg { open: span.open, close: span.close },
});
}
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
fn parse_cfg_attr_internal<'a>(
parser: &mut Parser<'a>,
sess: &'a Session,
features: Option<&Features>,
attribute: &Attribute,
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints)?;
let pred_span = pred_start.with_hi(parser.token.span.hi());
let cfg_predicate = AttributeParser::parse_single_args(
sess,
attribute.span,
attribute.get_normal_item().span(),
attribute.style,
AttrPath {
segments: attribute
.ident_path()
.expect("cfg_attr is not a doc comment")
.into_boxed_slice(),
span: attribute.span,
},
pred_span,
CRATE_NODE_ID,
features,
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
)
.ok_or_else(|| {
let mut diag = sess.dcx().struct_err(
"cfg_entry parsing failing with `ShouldEmit::ErrorsAndLints` should emit a error.",
);
diag.downgrade_to_delayed_bug();
diag
})?;
parser.expect(exp!(Comma))?;
// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);
while parser.token != token::Eof {
let lo = parser.token.span;
let item = parser.parse_attr_item(ForceCollect::Yes)?;
expanded_attrs.push((item, lo.to(parser.prev_token.span)));
if !parser.eat(exp!(Comma)) {
break;
}
}
Ok((cfg_predicate, expanded_attrs))
}

View file

@ -1,7 +1,7 @@
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_hir::RustcVersion;
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;

View file

@ -1,15 +1,12 @@
use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
use rustc_feature::{AttributeTemplate, template};
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};
use crate::target_checking::Policy::AllowSilent;
pub(crate) struct OptimizeParser;
@ -17,7 +14,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 +39,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 +53,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 +71,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 +95,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 +122,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 +154,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 +238,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 +286,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 +338,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 +358,14 @@ 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)),
AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass
Error(Target::Closure),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
}
@ -244,6 +373,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
pub(crate) struct UsedParser {
first_compiler: Option<Span>,
first_linker: Option<Span>,
first_default: Option<Span>,
}
// A custom `AttributeParser` is used rather than a Simple attribute parser because
@ -253,10 +383,10 @@ 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,
ArgParser::NoArgs => UsedBy::Default,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.expected_single_argument(list.span);
@ -289,7 +419,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;
}
}
@ -297,12 +427,29 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
ArgParser::NameValue(_) => return,
};
let attr_span = cx.attr_span;
// `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the
// circumstances are more complicated). While we're checking `used_by`, also report
// these cross-`UsedBy` duplicates to warn.
let target = match used_by {
UsedBy::Compiler => &mut group.first_compiler,
UsedBy::Linker => &mut group.first_linker,
UsedBy::Linker => {
if let Some(prev) = group.first_default {
cx.warn_unused_duplicate(prev, attr_span);
return;
}
&mut group.first_linker
}
UsedBy::Default => {
if let Some(prev) = group.first_linker {
cx.warn_unused_duplicate(prev, attr_span);
return;
}
&mut group.first_default
}
};
let attr_span = cx.attr_span;
if let Some(prev) = *target {
cx.warn_unused_duplicate(prev, attr_span);
} else {
@ -310,75 +457,222 @@ 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`
Some(match (self.first_compiler, self.first_linker) {
(_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
(Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
(None, None) => return None,
// If a specific form of `used` is specified, it takes precedence over generic `#[used]`.
// If both `linker` and `compiler` are specified, use `linker`.
Some(match (self.first_compiler, self.first_linker, self.first_default) {
(_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
(Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
(_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
(None, None, None) => return None,
})
}
}
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 OmitGdbPrettyPrinterSectionParser;
pub(crate) struct SanitizeParser;
impl<S: Stage> NoArgsAttributeParser<S> for OmitGdbPrettyPrinterSectionParser {
const PATH: &[Symbol] = &[sym::omit_gdb_pretty_printer_section];
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;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::OmitGdbPrettyPrinterSection;
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 })
}
}

View file

@ -1,11 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::template;
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() {

View file

@ -0,0 +1,144 @@
use super::prelude::*;
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 })
}
}
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;
}

View file

@ -0,0 +1,60 @@
use rustc_hir::attrs::{DebugVisualizer, DebuggerVisualizerType};
use super::prelude::*;
pub(crate) struct DebuggerViualizerParser;
impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
const PATH: &[Symbol] = &[sym::debugger_visualizer];
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(
List: &[r#"natvis_file = "...", gdb_script_file = "...""#],
"https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute"
);
type Item = DebugVisualizer;
const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let Some(l) = args.list() else {
cx.expected_list(args.span().unwrap_or(cx.attr_span));
return None;
};
let Some(single) = l.single() else {
cx.expected_single_argument(l.span);
return None;
};
let Some(mi) = single.meta_item() else {
cx.expected_name_value(single.span(), None);
return None;
};
let path = mi.path().word_sym();
let visualizer_type = match path {
Some(sym::natvis_file) => DebuggerVisualizerType::Natvis,
Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter,
_ => {
cx.expected_specific_argument(
mi.path().span(),
&[sym::natvis_file, sym::gdb_script_file],
);
return None;
}
};
let Some(path) = mi.args().name_value() else {
cx.expected_name_value(single.span(), path);
return None;
};
let Some(path) = path.value_as_str() else {
cx.expected_string_literal(path.value_span, Some(path.value_as_lit()));
return None;
};
Some(DebugVisualizer { span: mi.span(), visualizer_type, path })
}
}

View file

@ -1,12 +1,10 @@
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_feature::{AttributeTemplate, template};
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;
}

View file

@ -1,16 +1,18 @@
use rustc_attr_data_structures::AttributeKind;
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::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> {

View file

@ -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_attr_data_structures::lints::AttributeLintKind;
use rustc_attr_data_structures::{AttributeKind, InlineAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use rustc_hir::attrs::{AttributeKind, InlineAttr};
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,13 @@ 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 = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
@ -59,7 +71,12 @@ 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),
Allow(Target::Method(MethodKind::Inherent)),
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let reason = match args {

View file

@ -1,14 +1,21 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
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,438 @@ 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 items = match args {
ArgParser::List(list) => list,
// This is an edgecase added because making this a hard error would break too many crates
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
_ => {
cx.expected_list(cx.attr_span);
return None;
}
};
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 None;
}
}
// 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 }))
| (sym::as_dash_needed, Some(NativeLibKind::RawDylib { 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 None;
};
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
if !matches!(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 });
}
Some(LinkEntry {
span: cx.attr_span,
kind: kind.unwrap_or(NativeLibKind::Unspecified),
name,
cfg,
verbatim,
import_name_type,
})
}
}
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 { as_needed: None }
}
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),
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!(
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 +503,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 +511,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 +519,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 +527,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 +542,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 +576,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))
}
}

View file

@ -1,13 +1,16 @@
use rustc_attr_data_structures::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;
}

View file

@ -1,13 +1,10 @@
use rustc_attr_data_structures::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;
}

View file

@ -1,18 +1,14 @@
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
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 +27,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 +101,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 = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
@ -108,8 +113,79 @@ 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 local_inner_macros = match args {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { 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;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
}
}
}
ArgParser::NameValue(_) => {
let span = cx.attr_span;
let suggestions = cx.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 })
}
}

View file

@ -7,29 +7,36 @@
//! 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.
use std::marker::PhantomData;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol};
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;
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 debugger;
pub(crate) mod deprecation;
pub(crate) mod dummy;
pub(crate) mod inline;
@ -41,7 +48,9 @@ pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
pub(crate) mod path;
pub(crate) mod pin_v2;
pub(crate) mod proc_macro_attrs;
pub(crate) mod prototype;
pub(crate) mod repr;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;
@ -78,6 +87,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.
@ -115,6 +125,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;
@ -162,6 +174,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)
@ -246,6 +259,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;
@ -263,6 +277,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> {
@ -288,10 +303,12 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
type Item;
/// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute.
///
/// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
/// For example, individual representations from `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
/// 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;
@ -323,15 +340,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 {

View file

@ -1,12 +1,7 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
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,8 @@ 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 = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),

View file

@ -1,13 +1,11 @@
use rustc_attr_data_structures::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;
}

View file

@ -1,13 +1,25 @@
use rustc_attr_data_structures::AttributeKind;
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;
}

View file

@ -1,10 +1,4 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
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 {

View file

@ -0,0 +1,21 @@
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;
pub(crate) struct PinV2Parser;
impl<S: Stage> NoArgsAttributeParser<S> for PinV2Parser {
const PATH: &[Symbol] = &[sym::pin_v2];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Enum),
Allow(Target::Struct),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinV2;
}

View 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};

View file

@ -1,18 +1,13 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
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 {

View 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))
}

View file

@ -1,14 +1,9 @@
use rustc_abi::Align;
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_feature::{AttributeTemplate, template};
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 })
}
}

View file

@ -1,18 +1,23 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use super::prelude::*;
use super::util::parse_single_integer;
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage, parse_single_integer};
use crate::parser::ArgParser;
pub(crate) struct RustcMainParser;
pub(crate) struct RustcLayoutScalarValidRangeStart;
impl<S: Stage> NoArgsAttributeParser<S> for RustcMainParser {
const PATH: &'static [Symbol] = &[sym::rustc_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain;
}
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
pub(crate) struct RustcLayoutScalarValidRangeStartParser;
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartParser {
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)
@ -20,13 +25,14 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
}
}
pub(crate) struct RustcLayoutScalarValidRangeEnd;
pub(crate) struct RustcLayoutScalarValidRangeEndParser;
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser {
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 +46,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> {
@ -51,3 +58,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
Some(AttributeKind::RustcObjectLifetimeDefault)
}
}
pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser {
const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];
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!(NameValueStr: "N");
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::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))
}
}

View file

@ -1,12 +1,9 @@
use rustc_attr_data_structures::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;
}

View file

@ -1,18 +1,13 @@
use std::num::NonZero;
use rustc_attr_data_structures::{
AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_errors::ErrorGuaranteed;
use rustc_feature::template;
use rustc_span::{Ident, Span, Symbol, sym};
use rustc_hir::{
DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
};
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 {
@ -25,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>,
@ -47,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)
@ -59,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)
@ -86,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 {
@ -130,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() {
@ -141,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?;
@ -153,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;
}
@ -176,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 {

View file

@ -1,11 +1,4 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::AttributeLintKind;
use rustc_feature::{AttributeTemplate, template};
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 {
@ -22,8 +20,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(false, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@ -34,8 +31,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
@ -44,3 +40,59 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
})
}
}
pub(crate) struct ShouldPanicParser;
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 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 {
span: cx.attr_span,
reason: match args {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
cx.expected_string_literal(
name_value.value_span,
Some(name_value.value_as_lit()),
);
return None;
};
Some(str_value)
}
ArgParser::List(list) => {
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(single) = single.meta_item() else {
cx.expected_name_value(single.span(), Some(sym::expected));
return None;
};
if !single.path().word_is(sym::expected) {
cx.expected_specific_argument_strings(list.span, &[sym::expected]);
return None;
}
let Some(nv) = single.args().name_value() else {
cx.expected_name_value(single.span(), Some(sym::expected));
return None;
};
let Some(expected) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
Some(expected)
}
},
})
}
}

View file

@ -1,22 +1,22 @@
use core::mem;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
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;
}

View file

@ -1,11 +1,6 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
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
}

View file

@ -1,8 +1,16 @@
use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
use rustc_attr_data_structures::RustcVersion;
use std::num::IntErrorKind;
use rustc_ast::LitKind;
use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, sym};
use crate::context::{AcceptContext, Stage};
use crate::parser::{ArgParser, NameValueParser};
use crate::session_diagnostics::LimitInvalid;
/// 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.
@ -20,11 +28,8 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
}
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)
attr.is_doc_comment().is_some()
|| attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
@ -56,3 +61,63 @@ 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)
}
impl<S: Stage> AcceptContext<'_, '_, S> {
pub(crate) 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
}
}

View file

@ -4,73 +4,94 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use private::Sealed;
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
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::{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,
OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser,
UsedParser,
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::debugger::DebuggerViualizerParser;
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;
use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::pin_v2::PinV2Parser;
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,
RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcMainParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::IgnoreParser;
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;
macro_rules! group_type {
($stage: ty) => {
LazyLock<(
BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
)>
};
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) finalizers: Vec<FinalizeFn<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> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>;
type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
macro_rules! attribute_parsers {
(
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
@ -93,11 +114,11 @@ macro_rules! attribute_parsers {
}
};
(
@[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut finalizes = Vec::<FinalizeFn<$stage>>::new();
$(
{
thread_local! {
@ -105,11 +126,15 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
})));
accepts.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
});
}
finalizes.push(Box::new(|cx| {
@ -119,7 +144,7 @@ macro_rules! attribute_parsers {
}
)*
(accepts, finalizes)
GroupTypeInner { accepters:accepts, finalizers:finalizes }
});
};
}
@ -127,6 +152,7 @@ attribute_parsers!(
pub(crate) static ATTRIBUTE_PARSERS = [
// tidy-alphabetical-start
AlignParser,
AlignStaticParser,
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
@ -139,6 +165,9 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<DebuggerViualizerParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
@ -146,6 +175,8 @@ attribute_parsers!(
// tidy-alphabetical-start
Single<CoverageParser>,
Single<CrateNameParser>,
Single<CustomMirParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
@ -154,26 +185,38 @@ 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<RustcLayoutScalarValidRangeEndParser>,
Single<RustcLayoutScalarValidRangeStartParser>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
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>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<ConstTraitParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<ExportStableParser>>,
@ -184,16 +227,20 @@ 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<OmitGdbPrettyPrinterSectionParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PinV2Parser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
@ -213,24 +260,26 @@ mod private {
#[allow(private_interfaces)]
pub trait Stage: Sized + 'static + Sealed {
type Id: Copy;
const SHOULD_EMIT_LINTS: bool;
fn parsers() -> &'static group_type!(Self);
fn parsers() -> &'static GroupType<Self>;
fn emit_err<'sess>(
&self,
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed;
fn should_emit(&self) -> ShouldEmit;
fn id_is_crate_root(id: Self::Id) -> bool;
}
// allow because it's a sealed trait
#[allow(private_interfaces)]
impl Stage for Early {
type Id = NodeId;
const SHOULD_EMIT_LINTS: bool = false;
fn parsers() -> &'static group_type!(Self) {
fn parsers() -> &'static GroupType<Self> {
&early::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(
@ -238,11 +287,15 @@ 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
}
}
@ -250,9 +303,8 @@ impl Stage for Early {
#[allow(private_interfaces)]
impl Stage for Late {
type Id = HirId;
const SHOULD_EMIT_LINTS: bool = true;
fn parsers() -> &'static group_type!(Self) {
fn parsers() -> &'static GroupType<Self> {
&late::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(
@ -262,6 +314,14 @@ impl Stage for Late {
) -> ErrorGuaranteed {
tcx.dcx().emit_err(diag)
}
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
@ -279,8 +339,19 @@ pub struct Late;
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) shared: SharedContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
/// The outer span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^^^^ outer span
/// For attributes in `cfg_attr`, the outer span and inner spans are equal.
pub(crate) attr_span: Span,
/// The inner span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^ inner span
pub(crate) inner_span: Span,
/// Whether it is an inner or outer attribute
pub(crate) attr_style: AttrStyle,
/// The expected structure of the attribute.
///
@ -300,7 +371,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 !S::SHOULD_EMIT_LINTS {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
) {
return;
}
let id = self.target_id;
@ -363,6 +437,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
suggestions: self.suggestions(),
})
}
@ -373,6 +448,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
suggestions: self.suggestions(),
})
}
@ -383,6 +459,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
suggestions: self.suggestions(),
})
}
@ -393,6 +470,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
suggestions: self.suggestions(),
})
}
@ -404,6 +482,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
suggestions: self.suggestions(),
})
}
@ -416,6 +495,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
suggestions: self.suggestions(),
})
}
@ -427,6 +507,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
suggestions: self.suggestions(),
})
}
@ -439,6 +520,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
suggestions: self.suggestions(),
})
}
@ -449,6 +531,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
suggestions: self.suggestions(),
})
}
@ -459,13 +542,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
suggestions: self.suggestions(),
})
}
/// 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,
@ -477,13 +562,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
suggestions: self.suggestions(),
})
}
/// 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,
@ -495,13 +583,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
suggestions: self.suggestions(),
})
}
/// 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,
@ -513,11 +603,24 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
suggestions: self.suggestions(),
})
}
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span);
let attr_path = self.attr_path.clone();
let valid_without_list = self.template.word;
self.emit_lint(
AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list },
span,
);
}
pub(crate) fn suggestions(&self) -> Vec<String> {
// If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`,
// So don't display an attribute style in the suggestions
let style = (self.attr_span != self.inner_span).then_some(self.attr_style);
self.template.suggestions(style, &self.attr_path)
}
}
@ -548,7 +651,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.
@ -601,343 +704,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().0.get(parts.as_slice()) {
for (template, 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,
attr_path: path.get_attribute_path(),
};
accept(&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().1 {
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().0.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)
}

Some files were not shown because too many files have changed in this diff Show more